Well, since you have come here, I can guess you are looking for some improvements in your coding and code quality. That's great!
Let's take a look at it how we can do it better :
While using the stateful widgets we need to be mindful of rebuilding the widget tree which significantly impacts the performance of your application. Here we will look at an example ( there can be many ways to achieve the same thing). In this example, we will make use of ValueNotifier
and ValueListenableBuilder
Setup your Flutter project
Create a Flutter app
flutter create example_app
Run your app
Now, we are done setting up the Flutter app, let's dive into the example.
Coding Example
In my main.dart file,
import 'package:flutter/material.dart';
void main() async {
runApp( const MainApp()); // entry point of your application
}
// In this example, we have a stateless widget
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MySwitch() // MySwitch() is a stateful widget
),
),
);
}
}
What is ValueNotifier and ValueListenableBuilder?
ValueNotifier
is a class that has a single generic type
( which means it can't hold more than one value) that specifies the type of value it holds. When the value it holds is changed it will trigger
all the listeners
listening to the changed value.
ValueNotifier<bool> switchValue = ValueNotifier<bool>(false);
Here I have created a ValueNotifier
that stores a boolean
value that has an initial value of false
.
We can use this ValueNotifier
which in this case is switchValue
to listen to the value and trigger the change of value to different available listeners.
ValueListenableBuilder
is a widget that can listen
and update the UI based on the value
.
ValueListenableBuilder<bool>(
// valueListenable accepts the valueNotifier we have just created
valueListenable: switchValue,
// builder gives the current context, current value and the child it has to return
builder: (BuildContext context, bool value, Widget? child) {
return Switch(
// switch takes the boolean value which is returned by builder method
value: value,
onChanged: (newValue) {
// onChanged return the value opposite to current value. eg
// if the current value is false, it will return true
// Now we access the value inside of valueNotifier and change it with the value we got from onChanged Method
switchValue.value = newValue;
/// when this value changed the builder method is re-run
},
);
},
);
Now that we understand how it works, let's wrap it up, in a line.
ValueListenableBuilder
listens to the ValueNotifier
that has a single generic value, on change of which ValueListenableBuilder
will re-run the builder
method, which reflects the change in UI without rebuilding the whole widget tree
.
Full Code:
import 'package:flutter/material.dart';
void main() async {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(child: MySwitch()),
),
);
}
}
class MySwitch extends StatefulWidget {
@override
_MySwitchState createState() => _MySwitchState();
}
class _MySwitchState extends State<MySwitch> {
ValueNotifier<bool> switchValue = ValueNotifier<bool>(false);
@override
void dispose() {
switchValue.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
print("Widget rebuilding");
return ValueListenableBuilder<bool>(
valueListenable: switchValue,
builder: (BuildContext context, bool value, Widget? child) {
return Switch(
value: value,
onChanged: (newValue) {
switchValue.value = newValue;
},
);
},
);
}
}
I hope you got value out of this, if you did, you can subscribe to the newsletter that I will put out with updates and best practices.😉 For more, content like this, visit Flutter World.