Learning Flutter — Part 2

Kapil Vij
4 min readAug 10, 2024

--

Part 1 link : https://kapilvij.medium.com/learning-flutter-part-1-7239aac81bba

What are States ?
When you want your app to change based on user interaction or time, you have to render updated information on UI. This is done via states and for this we have Stateful widgets in Flutter.

Flutter provides “setState()” method to inform the UI to rerun build method.

LifeCycle of Stateful widget :

“initState” is called only in entire lifecycle of widget and is useful when u want to initilize logic before ui is built.

“build” is called everytime when setState is called or parent widget has been recreated.

“dispose” is used to put logic which is required to free up memory or reseting values or clearing expensive tasks. This is called before state object is permanently removed.

Buttons :
1. Elevated Button
2. Outlined Button
3. TextButton

Button’s expect onPressed method to be override, if null is returned here then it looks like disabled.

GestureDetector and InkWell :

Both are meant for same purpose to handle action like click, long press etc. InkWell also helps in showing ripple kind of ui when there is interaction with widgets using splashColor property.

InkWell(
splashColor: Colors.red,
onTap: () {
print('Tapped');
},
child: Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
)


GestureDetector(
onTap: () {
print('Tapped');
},
child: Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
)

TextField Widget — equivalent in Edittext in Android

We need to create controller which is an instance of “TextEditingController” and based on that you can access the value of content when required.

“obscureText” property when set to true will replace content with astericks and is used for password fields.

final userNameController = TextEditingController();

........ pass controller in Textfield ..........

TextField(
controller: userNameController,
obscureText: true,
decoration: InputDecoration(
hintText: 'Enter your name',
),
)

....... to get values after editing is done .....

print(userNameController.text);

TextField also has property keyboardType which helps defining the type of keyboard, like number, text or multiline.

TextField(
keyboardType: TextInputType.multiline,
maxLines: 5,
minLines: 1,
controller: userNameController,
)

TextFormField Widget : Same as above but helps with validator

This helps in moving all field related validations at one place using forms. And this can be validated at once when user clicks on CTA to proceed.

  final _formKey = GlobalKey<FormState>();

........

Form(
key: _formKey,
child: Column(
children: [
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
],
),
)

..........
GestureDetector(
onTap: () {
if(_formKey.currentState != null && _formKey.currentState!.validate()) {
print('Form is valid');
} else {
print('Form is invalid');
}
}
);

Navigator :
Manages stack of routes and provides to manage it via 2 ways, Declarative api and Imperative API.

It expects a context and a Route in push and to pop back just a context as shown below.

Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Container(
child: Text('Login'),
)));
Navigator.pop(context)

or
Navigator.maybePop(context) // used when u r not sure if its root page.

Named Routes:
You can also define routes in one place in app and use namedRoutes everywhere to prevent writing boiler plate code.

............ Add routes in app ...........

return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
routes: {
'/listPage' : (context) => const DynamicList(),
},
);

// ......... consume it like below .....

Navigator.pushNamed(context, /listPage');

// ......... Below is used to replace current page with new one.....

Navigator.pushReplacementNamed(context, /listPage');

Above “popAndPushNamed” can also be used in place of “pushReplacementNamed” to show screen with animation that is getting popped first and then new one is added.

Handling Arguments in Navigator:

ModalRoute is used to access the arguments. You can cast the value in the type as passed in Navigator

Navigator.pushNamed(context, /listPage', arguments : "");

..... access with Build method of widget ...

final userName = ModalRoute.of(context)!.settings.arguments as String;

Reading file from assets
Add a file in assets folder available on root.

Then add below line in your pubspec.yaml

  assets:
- assets/mock_response.json

Use below code to load file from assets using async await

_loadFromFile() async {
final response = await rootBundle.loadString('assets/mock_response.json');
List<dynamic> responseData = jsonDecode(response);

List<Book> books = responseData.map((e) => Book.fromJson(e)).toList();
return books;
}


------- Same code using 'then' , acts as callback method -----

_loadFromFile() {
rootBundle.loadString('assets/mock_response.json').then((response) {
List<dynamic> responseData = jsonDecode(response);

List<Book> books = responseData.map((e) => Book.fromJson(e)).toList();
});
}

Calculating width of Parent Widget :

MediaQuery.of(context).size.width;

— — — — — — — — — — — — — That’s all folks — — — — — — — — — — — — — —

Lets catchup in next part of this blog !!

--

--