Introduction
In this article, I would be discussing a very important concept in flutter, which is StateFulWidget
, If you have read my first article, I created all widgets there, which are Stateless whose definition according to Flutter website is that widget is immutable, meaning that their properties couldn’t be changed – all values are final, once displayed.
So why do we use of such language/ framework, when we can’t change anything on the Mobile screen? The answer lies in Flutter developer excellent architectural approach which is StateFulWidget
, which maintains state that might change during the lifetime of the widget.
Background
Implementing a stateful widget requires at least two classes which are:
- A
StatefulWidget
class that creates an instance of a State
class - The
StatefulWidget
class is, itself, immutable, but the State
class persists over the lifetime of the widget
We discuss about it more during this tutorial.
Aim of the Tutorial
I am going to create a small mobile application which will contain the following things:
AppBar
displaying the application name TextField
to accept user input ButtonBar
with two buttons
Clear button
: to clear text in TextBox
Add City button
: to add user input in the list view
ListView
to persist whatever user input is
Overall, the basic idea it to create a list of cities visited in app, this will show how StateFulWidget
refreshes screen, when there is change in any value of the widget/class.
Using the Code
So let's dive in the application directly, here are the steps to follow, if you would like to make your hands dirty:
- Open Android Studio and create a new flutter project by the name
flutter2_sfw
(short form for StateFulWidget
). If using Visual Studio Code, use command flutter create flutter2_sfw
in terminal window. Please make sure Flutter bin path should be in your environment path variable. - Once the project is created, following would be basic structure of the solution:
- Now in main.dart file under lib folder, delete everything and write the following code:
import 'package:flutter/material.dart';
import 'package:flutter2_sfw/widgets/mainpage.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.amber,
),
home: new MainPage(title: 'Flutter 2 - Add City'),
);
}
}
Explanation of Code
- Here, we are creating our main widget, which would be the starting point of our mobile app.
MyApp
is StatelessWidget
which will host our StateFulWidget
as the first page, that's why we are passing MainPage
(to be created) as object:
- Now right click on lib
folder and click on new and add package to folder by name widgets. - Add new dart file by name mainpage.dart, which will have code of our Stateful widget.
- Difference between
StateFul
and Stateless
widget, for creation of StateFulWidget
, you require atleast two classes, one the actual page and other class holding state of page.
class MainPage extends StatefulWidget {
MainPage({Key key, this.title}) : super(key: key);
final String title;
@override
_MainPageState createState() => new _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
));
}
Explanation of Code
- Here, we create
MainPage
class which is inheriting from StateFulWidget
- We override
createState()
function and provide it with linked state class _MainPageState
class which is extending generic state of MainPage
, would contain the code for building up the UI - If we run our project at this point, this would be view in
Emulator
:
- Now it's time to add
TextField
and the buttons on the body of scaffold from the above step, the build function would look like this:
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new ListView(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
children: <Widget>[
new TextField(
decoration: InputDecoration(
filled: true,
labelText: 'City Name',
),
controller: _cityNameController,
),
new ButtonBar(
children: <Widget>[
new RaisedButton(
onPressed: () {
_cityNameController.clear();
},
child: new Text('Clear'),
),
new RaisedButton(
child: new Text('Add City'),
onPressed: _onAddCityBtnPressed,
color: Colors.amber,
)
],
),
]
)
);
}
New Class Members (_MainPageState)
final TextEditingController _cityNameController = TextEditingController();
final List<Widget> _lstText = new List<Widget>();
_onAddCityBtnPressed() Function
This function will be called when we press Add City button in the application.
_onAddCityBtnPressed() {
setState(() {
_lstText
.add(
new Text("${_lstText.length + 1} ${_cityNameController.text}",
textAlign: TextAlign.justify,
style: new TextStyle(fontWeight:FontWeight.bold),));
_cityNameController.clear();
});
Explanation of Code
- Here, we have created a
ListView
control, it's a scrolling control, allowing you to put multiple widgets which could be scrolled vertically. Don't worry if you don't understand it now, I would be writing a separate article for ListView
, discussing all prominent properties and methods provided by it. - Since it could take multiple children, first children we would be adding would be of
TextField
, we have two properties of TextField
here:
controller -
This will act as object for setting and getting values from the screen. Here, we are passing class member _cityNameController.
decoration -
This property would define UI of textfield
, I provided labelText
which is 'Add City' and filled
as true
- Second child is
ButtonBar
, which provides template for adding multiple buttons, I have created two RaisedButton
, first one would clear content of the TextField
and other button would add whatever text present in TextField
into local list by name _lstText
Clear Button
: OnPressed
will call _cityNameController.clear();
, which will clear textField
control. Add City Button
: OnPressed
will call _onAddCityBtnPressed()
method, whose working I will tell in the next step.
_onAddCityBtnPressed()
- would add whatever is present in the TextField
into the _lstText
and clear the textField
. _lstText
is of type List
, you can read more about it from my other article here. - If you haven't noticed, when we are updating the
_lstText
, is wrapped inside the setState()
function, it is there to indicate to framework, some objects are updated and you need to refresh state.
- Now it is time to add magic, till completion of point 7, we are able to take user input and store it in class variable, however nothing is displayed on the screen, even after we set state and make framework aware that we are updated something. Now it's time to add our
ListView
which will update itself whenever the _lstText
is updated. I have created two functions to handle this scenario, add getListViewBuilder
() function in the build function of _MainPageState
and I have added these two functions for creating ListView
:
ListView getListViewBuilder() {
return new ListView.builder(
shrinkWrap: true,
itemCount: _lstText.length,
padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
itemBuilder: getListItems,
);
}
Widget getListItems(BuildContext context, int index) {
return _lstText[index];
}
Explanation of Code
- Here, we created
ListView
with the help of ListView.builder
by passing itemCount
and callback function for itemBuilder
property. getListItems()
function would return the widget based on index passed by callback function to it.
- Final Run:
Here, we have reached the end of this article. Please let me know your comments in the message board. Thanks!
Points of Interest
Please go through these articles. It might give a headway, where the wind is actually flowing:
- Flutter — 5 reasons why you may love it
- What’s Revolutionary about Flutter
- Why Flutter Uses Dart
- Github: https://github.com/thatsalok/FlutterExample/tree/master/flutter2_sfw
Flutter Tutorial
- Flutter Getting Started: Tutorial #1
Dart Tutorial
- DART2 Prima Plus - Tutorial 1
- DART2 Prima Plus - Tutorial 2 - LIST
History
- 09-July-2018: First version