Reputation: 4808
I have two widgets in a column. One is Text and second is TextButton. What i want that if i click on button then the Text widget rebuild only not the whole page.
I am new to flutter how can i achieve this? If i convert this to a statful widget and call setState method then whole page will be rebuild. but i want to know any trick to do rebuild only a single widget out of whole page.
class Page3 extends StatelessWidget {
Color color = Colors.red;
changeColor() {
// do something to rebuild only 1st column Text not the whole page
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page3'),
),
body: Column(
children: [
//First widget
Text(
'Title',
style: TextStyle(color: color),
),
//Second widget
TextButton(
onPressed: () => changeColor(),
child: Text('change color of title'),
)
],
));
}
}
Upvotes: 0
Views: 1979
Reputation: 1469
Please refer to below code
ValueListenableBuilder
widget. It is an amazing widget. It builds the widget every time the valueListenable value changes. Its values remain synced with there listeners i.e. whenever the values change the ValueListenable listen to it. It updates the UI without using setState()
or any other state management technique
.
In Dart, a ValueNotifier
is a special type of class that extends a ChangeNotifer . ... It can be an int , a String , a bool or your own data type. Using a ValueNotifier improves the performance of Flutter app as it can help to reduce the number times a widget gets rebuilt.
ValueListenableBuilder
will listen for changes to a value notifier and automatically rebuild its children when the value changes.
For more info refer to this link description
Solution 1
class Page3 extends StatelessWidget {
Color color = Colors.red;
final ValueNotifier<bool> updateColor = ValueNotifier(false);
changeColor(Color changedColor) {
// do something to rebuild only 1st column Text not the whole page
color = changedColor;
updateColor.value = !updateColor.value;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page3'),
),
body: Column(
children: [
//First widget
ValueListenableBuilder<bool>(
valueListenable: updateColor,
builder: (context, val, child) {
return Text(
'Title',
style: TextStyle(color: color),
);
}),
//Second widget
TextButton(
onPressed: () => changeColor(Colors.purple),
child: Text('change color of title'),
)
],
));
}
}
Solution 2
In ValueListenable
we pass our created ValueNotifier
variable whose changes will be notified and in builder we will return a widget that will be reflected every time when the value of ValueNotifier
will be changed.
class Page3 extends StatelessWidget {
// Color color = Colors.red;
final ValueNotifier<Color> updateColor = ValueNotifier(Colors.red);
changeColor(Color changedColor) {
// do something to rebuild only 1st column Text not the whole page
updateColor.value = changedColor;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page3'),
),
body: Column(
children: [
//First widget
ValueListenableBuilder<Color>(
valueListenable: updateColor,
builder: (context, val, child) {
return Text(
'Title',
style: TextStyle(color: val),
);
}),
//Second widget
TextButton(
onPressed: () => changeColor(Colors.purple),
child: Text('change color of title'),
)
],
));
}
}
Upvotes: 2
Reputation: 124
You need to understand how setState works.
Lets assume you have a class named HomeScreen
, within the homescreen you are overriding the build
method to build your own widgets.
Widget build(BuildContext context){
return Column(
children:<Widget> [
FirstTextWidget();
SecondTextWidget();
ThirdTextWidget(),
])
}
when you call SetState function within that "homesceeen" class, the homescreen class itself calls the build
method again and all of componenets you have returned within build function get re-rendered. Every text within homescreen class gets rerendered.
So whats the solution?
The Solution is separate your stateful widget with different class so that only those widgets gets rerendered when needed not whole. I will prefer you to use State Management plugin like Provider, bloc etc.
Upvotes: 0
Reputation: 14775
Try below code hope its help to you. you must used StateFulWidget
for that
Create one bool variable
bool isButtonPressed = true;
Your widgets:
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Title',
style: isButtonPressed
? TextStyle(
color: Colors.black,
fontSize: 20,
)
: TextStyle(
color: Colors.green,
fontSize: 20,
),
),
),
TextButton(
child: new Text('Change color'),
onPressed: () {
setState(() {
isButtonPressed = !isButtonPressed;
});
},
),
],
),
Your Screen without button pressed:
Your Screen with button pressed:
Upvotes: 0
Reputation: 762
setState
function can not be called inside StatelessWidget
widget. if you want to rebuild the widget tree, you have to convert it to StatefulWidget
.
This is what you can do.
class Page3 extends StatefulWidget {
const Page3();
@override
_Page3State createState() => _Page3State();
}
class _Page3State extends State<Page3> {
Color color = Colors.red;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
Text(
'Title',
style: TextStyle(color: color),
),
TextButton(
onPressed: () => changeColor(),
child: Text('change color of title'),
)
]),
);
}
changeColor() {
setState(() {
color = Colors.green;
});
}
}
If you want to rebuild the Text
widget without rebuilding the whole Page3
then you need to got for 'state management' solution.
Upvotes: 0
Reputation: 1541
Here's the code of what you need to do
class Demo extends StatefulWidget {
const Demo({Key? key}) : super(key: key);
@override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> {
var isTextChanged = false;
Void changeColor() {
setState(() {
isTextChanged = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page3'),
),
body: Column(
children: [
//First widget
Text(
'Title',
style: TextStyle(color: isTextChanged ? Colors.red : Colors.black),
),
//Second widget
TextButton(
onPressed: () => changeColor(),
child: Text('change color of title'),
)
],
));
}
}
Upvotes: 0