Keys & Validations
Ok, now we would like to add some validation to this form - we do not want to let users save notes with empty title or description. To do it, we have to use Key.
Keys are an identifier for Widgets. A new widget will only be used to update an existing element if its key is the same as the key of the current widget associated with the element. As you may have noticed earlier, there is no method like getState in StatefulWidget. The proper way to get the state of a widget is to use its key.
Keys are a bit more advanced stuff. Please check out official documentation to read more about them:
Ok, let’s start with wrapping our TextFields into Form. Select a Container widget, press ALT + ENTER and in the popup menu choose ‘Wrap with new widget’, then type Form to wrap it into a form. Now, your widget should look like this:
Ok, now it is time to add validation. Flutter has a mechanism for form validation. Start with creating new method. This method will be called after pressing our Floating Action Button:
Ok, now we need to add our key - we need it to accessing form state. We also need to add fields for holding description and title value:
Ok, now let’s fill _submit() method:
Ok, the last thing we need to do is adding validator for our TextFormFields:
Your final state should look like this:
Let’s summarize the code above:
- _formKey gives us access to current form state
- _formKey.currentState.validate() runs validator for every form field. If any of them is not returning null, this method is returning false
- validator: (val) => val.isNotEmpty ? null : "Title must not be empty", means, that if condition is not true, our form field is displaying error message
- onSaved: (val) => … is called on every form field after calling _formKey.currentState.save() method
Run your application. If any of form fields are empty, you will see an error message.
Unfortunately, flutter does not have built in support for persisting data. If you would like to use database or preferences, you need to use a specific plugin. Here is a plugin for supporting preferences: https://pub.dartlang.org/packages/shared_preferences, and this one will give you access to sqlite database: https://github.com/tekartik/sqflite For this demo, we will use the second option.
First of all, we need to add required dependencies to our project. Open your pubspec.yaml file and modify it as shown below:
On the official website (https://github.com/tekartik/sqflite) you will find information how to use sqflite, that’s why I do not want to explain it right now. All we need to do, is create new dart file called db_manager.dart and put this code inside:
As you may have noticed, apart from DbManager class we’ve also created the Note class, which is going to represent an instance of a note.
Navigation & Dependency Injection
Currently, our application is starting from the note details screen. We would like to start with notes list screen, and make it possible for users to navigate to our current screen. To support navigation, we have to use routes.
Flutter provides two ways of creating routes: we can define routes in our MaterialApp widget and accessing them using names, or we can create them dynamically. Here is example of routes defined in MaterialApp widget:
To navigate to this kind of widget, simply call:
As you can see, these kinds of routes can help you with implementing deep linking among other things. The only one problem with these routes is that you cannot pass data when you are navigating to widget. Fortunately, this widget gives you this feature: https://github.com/goposse/fluro
Remember that in routes, like mentioned above, navigating to PasswordScreen will also trigger creation of a SettingsScreen (because it will navigate to /settings, and then to /password).
Second way to create a route is to simply call:
Navigator.push(context,new MaterialPageRoute(builder: (context) => new SecondScreen()));
If you would like to navigate back, you need to call:
You can read more about routes here: https://flutter.io/cookbook/navigation/navigation-basics/
Implementing navigation in our example
Ok, now we would like to create a main screen with a list of notes, and add the option to navigate to note details screen. We will start with an empty screen with navigation. Open your main.dart file and modify it this way:
Build and run your application. You should be able to navigate from the main screen to the NoteDetailsWidget. After saving the form, you will be redirected back to the main screen.
Now we would like to create an instance of DbManager, and pass it to both our widgets. In Flutter, we are creating our widgets using constructors, so we can pass dependencies directly to our constructor. As you can remember, by defining routes in our MaterialApp widget we cannot pass dependencies. Fortunatelly, we can do it using Fluro library: https://github.com/goposse/fluro
Should you need to use additional dependency injector, you can use this library: https://github.com/google/inject.dart It is similar to Google Dagger.
Now, let’s start persisting our notes. Open note_details_widget.dart and make it depend on DbManager:
Now do the same with NoteDetailsWidgetState:
The last thing we need to do, is to persist the note. Add these lines to your _submit() method:
Now your note will be persisted in database whenever the save button is pressed.