flutter build only a single widget - flutter

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'),
)
],
));
}
}

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'),
)
],
));
}
}

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'),
)
],
));
}
}

setStatefunction 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.

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:

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.

Related

Widget not updating in flutter?

This method is not updating the widget
Widget displayAppropriateWidget() {
if (isParsedCorrectly) {
_widgetToDisplay = displaySecondHalf;
} else {
_widgetToDisplay = displayFirstHalf;
}
setState(() {});
return _widgetToDisplay;
}
Even when the bool in other file and this file updates widget doesn't update. I can only see the changes only after I hot reload
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const PreferredSize(
preferredSize: Size.fromHeight(100),
child: CustomAppBar("Sign-Up"),
),
body: Align(
alignment: Alignment.center,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: 100, bottom: 5),
child: Image.asset(
'assets/images/college.png',
width: 200,
fit: BoxFit.cover,
),
),
displayAppropriateWidget(),
],
),
),
);
}
}
You need to listen to the changes of that bool by injecting it into the Widget in some way. For example by:
Passing it into the widget as an argument - which you can do if the bool is declared in a Widget higher up in the widget tree. Then you can call setState in that parent Widget once you've updated the bool. But setState can only be used in StatefulWidget. More on using setState here: https://docs.flutter.dev/development/ui/interactive and here: https://api.flutter.dev/flutter/widgets/State/setState.html
Or by using some sort of state management. Here's a tutorial for that: https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple
Also, your function displayAppropriateWidget calls setState, which you should not do from inside a Widgets build method - since that would mean rebuilding every time you build, causing infinite rebuilds.
Another tip is to try to avoid building Widgets from functions, as it's not as performant and can lead to some hard to identify bugs: https://www.youtube.com/watch?v=IOyq-eTRhvo
Please refer to below example code of updating value from another class
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.
ValueNotifer & ValueListenableBuilder can be used to hold value and update widget by notifying its listeners and reducing number of times widget tree getting rebuilt.
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
final ValueNotifier<int> counter = ValueNotifier(0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: FloatingActionButtonClass(),
);
}
}
class FloatingActionButtonClass extends StatelessWidget {
void _incrementCounter() {
counter.value++;
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
body: Center(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyHomePage()),
);
},
child: Text("Floating Action Button"),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({
Key? key,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Example"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
ValueListenableBuilder(
valueListenable: counter,
builder: (context, value, child) {
return Text(
counter.value.toString(),
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
),
);
}
}

How to change the Value of a Widget in Flutter through dart

i would like to know o can i change the value of a child widget in flutter if a certain condition is met, for example the color of an icon in the trailing
Here is some pseudo-code:
if(condition){
trailing Icon(Icons.favorite).color = Colors.red[500]
}else{
trailing = Icon(Icons.Favorite).color = Colors.blue[300]
}
Thank you.
you wanna something like this?
if yes, try this code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool colorIndex = true;
void _changeColor(val) {
setState(() {
this.colorIndex = val;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_changeColor(!colorIndex);
},
child: Icon(Icons.touch_app),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Card(
child: ListTile(
title: Text('Click FAB to change color'),
trailing: Icon(
Icons.favorite,
color: colorIndex ? Colors.red[500] : Colors.blue[300],
),
),
),
),
),
),
);
}
}
You can change anything under any condition you define. The most simple example is using setState to update a value that can be inspected during build. This value could change under any condition you like. Calling setState will trigger the UI to rebuild (calls the build method).
Here is a Widget. It displays the text "Hello, World!" in the center of the screen. The AppBar at the top has a leading IconButton Widget. When the IconButton is pressed, it will toggle the color of the "Hello, World!" text. It does this by updating the state of Widget and toggling the value of the blue variable. The condition is: if (blue) {} or "if blue is equal to true then change the color." During the build of the UI, the code inspects the value of blue and determines what TextStyle to apply to the "Hello, World!" text.
import 'package:flutter/material.dart';
class ColorChangeWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => _ColorChangeWidgetState();
}
class _ColorChangeWidgetState extends State<ColorChangeWidget> {
bool blue = false;
#override
Widget build(BuildContext context) {
TextStyle style = TextStyle(color: Colors.black);
if (blue) {
style = TextStyle(color: Colors.blue);
}
return Scaffold(
appBar: AppBar(
title: Text("Test"),
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
blue = !blue;
});
},
),
),
body: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(8.0),
child: Text("Hello, World!", style: style)
)
);
}
}

How to copy the value of a Widget Text to another using onPressed property of a button

I need to copy a value of a Text Widget and copy this to another.
I tried to this using keys, but I don't know how to acess the Text Widget in this case.
Is it possible to do in Flutter, using the onPressed property?
import 'package:flutter/material.dart';
class TextWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Value to be copied",
key: Key('text1')
),
RaisedButton(
onPressed: (){
// code here
},
child: Text("Copy value"),
),
SizedBox(height: 40),
Text(
"",
key: Key('text2')
)
],
),
),
);
}
}
Answering your question directly: you can access text inside Text widget using its data property.
Text widget = Text('text value');
String text = widget.data;
print(text); // text value
Next, you can't access widgets by their key properties. At least you shouldn't, because they were designed for different purpose: here's a video and an article about keys in Flutter.
What you can do here is turn your TextWidget from StatelessWidget into StatefulWidget and render contents of your second Text based on the state. Good introduction into what the state is and why you should use it can be found on official Flutter website: Start thinking declaratively.
Then you can save your first Text widget in a variable and then access its contents directly using data property update, then update state of the whole widget.
Example 1 on DartPad
More canonical and in general preferrable approach is to render contents of both buttons based on the state and get desired text from state variable and not from the widget itself, as proposed by Sebastian and MSARKrish.
Example 2 on DartPad
Note that you can't change data attribute of a Text widget imperatively, like you would do in JavaScript DOM API with innerText:
_textWidget.data = "New text"; // Doesn't work
because its data is final. In Flutter you have to think declaratively, and it worth it.
Try this
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String _text = "Value to be copied";
bool _buttonToggle;
#override
void initState() {
super.initState();
_buttonToggle = false;
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_text),
SizedBox(height: 40),
RaisedButton(
onPressed: _toggle,
child: Text("Copy value"),
),
Switch(
value: _buttonToggle,
onChanged: (_) => _toggle(),
),
SizedBox(height: 40),
Text(_buttonToggle ? _text : '')
],
);
}
void _toggle() {
setState(() => _buttonToggle = !_buttonToggle);
}
}
class TextWidget extends StatefulWidget {
#override
_TextWidgetState createState() => _TextWidgetState();
}
class _TextWidgetState extends State<TextWidget> {
String text1Value = "text to be copied";
String text2Value = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
text1Value,
),
RaisedButton(
onPressed: () {
setState(() {
text2Value = text1Value;
});
},
child: Text("Copy value"),
),
SizedBox(height: 40),
Text(
text2Value,
)
],
),
),
);
}
}

SwitchListTile not turning on in a Dialog

I am trying to create a workable switchlist in a Dialog. The Switch with icon, title shows but is not turning on.
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
FlatButton.icon(
icon: Icon(AntDesign.sound),
label: Text('Sound Options'),
onPressed: () {
setState(() {
showDialog(
context: context,
child: SimpleDialog(
children: <Widget>[
SimpleDialogOption(
child: SwitchListTile(
title: Text('Mute'),
secondary: Icon(AntDesign.sound),
value: _mute,
onChanged: (bool value) {
setState(() {
_mute = value;
});
}),
),
],
));
});
},
)
],
),
),
),
);
}
setState rebuilds your build method and change the _mute value, but the SimpleDialog is open in another context, so that dialog is not rebuild. You should make a new StatefulWidget with the AlertDialog and setState when changing the value there
class Class1 extends StatefulWidget {
#override
_Class1State createState() => _Class1State();
}
class _Class1State extends State<Class1> {
bool _mute = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
FlatButton.icon(
icon: Icon(AntDesign.sound),
label: Text('Sound Options'),
onPressed: () {
showDialog(
context: context,
builder: (context) => MyDialog(_mute)
);
},
)
],
),
),
),
);
}
}
class MyDialog extends StatefulWidget {
bool mute;
MyDialog(this.mute);
#override
_MyDialogState createState() => _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
#override
Widget build(BuildContext context) {
return SimpleDialog(
children: <Widget>[
SimpleDialogOption(
child: SwitchListTile(
title: Text('Mute'),
secondary: Icon(AntDesign.sound),
value: widget.mute,
onChanged: (bool value) {
setState(() {
widget.mute = value;
});
}),
),
],
);
}
}
Another few tips, use builder instead of child with showDialog, child is deprecated, don't wrap the whole SimpleDialog in a setState, you should just wrap the value you're going to change, wrapping the dialog won't make it rebuild with the new value.
From the documentation
Calling setState notifies the framework that the internal state of
this object has changed in a way that might impact the user interface
in this subtree, which causes the framework to schedule a build for
this State object.
the setState you use rebuilds the subtree of your whole Scaffold Stateful Widget, but the Dialog is open in another tree, it's not part of that subtree, If you use Android Studio there is a flutter inspector (and I believe Visual Studio has it too), you can see there that the dialog and your StatefulWidget come from Material App (the dialog is not part of the subtree so it won't rebuild)

Flutter: Update state of sibling widget

How do you update the state of a sibling widget in Flutter?
For example, if I have a rectangle widget, I can change its color from within the widget by calling setState() and changing the color variable (as the "Turn Green" button does below). But, say I wanted a button outside of the rectangle that would change its color. How do I communicate to the Rectangle that it's time to change its color, and what color to change to?
Here is my sample code. When the user presses the "Turn Blue" button, I'd like the rectangle to turn blue, but I don't have access to its state from the sibling widget.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Hello Rectangle',
home: Scaffold(
appBar: AppBar(
title: Text('Hello Rectangle'),
),
body: Column(
children: <Widget>[
HelloRectangle(),
FlatButton(
child: Text('Turn Blue!',
style: TextStyle(fontSize: 40.0),
textAlign: TextAlign.center,
),
onPressed: () {
// How to update state of the Rectangle from here?
},
),
]
),
),
),
);
}
class HelloRectangle extends StatefulWidget {
#override
HelloRectangleState createState() {
return new HelloRectangleState();
}
}
class HelloRectangleState extends State<HelloRectangle> {
Color _color;
#override
void initState() {
super.initState();
_color = Colors.red;
}
#override
Widget build(BuildContext context) {
return Center(
child: Container(
color: _color,
height: 400.0,
width: 300.0,
child: Center(
child: FlatButton(
child: Text('Turn Green!',
style: TextStyle(fontSize: 40.0),
textAlign: TextAlign.center,
),
onPressed: () {
// I can update the state from here because I'm inside the widget
setState(() {
_color = Colors.green;
});
},
),
),
),
);
}
}
The rule of thumb is that you can't access the state of any Widget that isn't above you in the hierarchy. So, basically we need to move the state (color) up to an ancestor. Introduce a StatefulWidget that builds the Scaffold or Column and store the rectangle color there. Now the rectangle widget no longer needs to store the color, so can become a stateless widget - and you can pass the color in through the constructor. Both onPressed callbacks can now call a method on the new StatefulWidget which calls setState. (You could pass that method down to the rectangle widget, amongst other ways.)
There are two good introductions to best practise here and here.
Here is basic example how to do it from outside.
import 'package:flutter/material.dart';
void main() {
runApp(
new MyApp(),
);
}
class MyApp extends StatefulWidget {
const MyApp({
Key key,
}) : super(key: key);
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
MaterialColor _color = Colors.green;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Hello Rectangle',
home: Scaffold(
appBar: AppBar(
title: Text('Hello Rectangle'),
),
body: Column(children: <Widget>[
HelloRectangle(_color),
FlatButton(
child: Text(
_color == Colors.green ? "Turn Blue" : "Turn Green",
style: TextStyle(fontSize: 40.0),
textAlign: TextAlign.center,
),
onPressed: () {
setState(() {
_color = _color == Colors.green ? Colors.blue : Colors.green;
});
},
),
]),
),
);
}
}
class HelloRectangle extends StatefulWidget {
final Color color;
HelloRectangle(this.color);
#override
HelloRectangleState createState() {
return new HelloRectangleState();
}
}
class HelloRectangleState extends State<HelloRectangle> {
HelloRectangleState();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Center(
child: Container(
color: widget.color,
height: 400.0,
width: 300.0,
),
);
}
}