Is it possible to call setState()
of particular widget (embedded in other widgets) from other widgets onPressed()
method so only that widget is redrawn?
I want to click on the button and see the state of "MyTextWidget" to change. The rest of the layout is same, nothing changes there so it should not be rewritten.
This is my code:
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Timer', theme: new ThemeData( primaryColor: Colors.grey.shade800, ), home: new MyHomePage(), ); } } class MyHomePage extends StatelessWidget { int _seconds = 1; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("title"), ), body: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ MyTextWidget(), //just update this widget Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ IconButton( icon: Icon(Icons.add_circle), onPressed: _addPressed, iconSize: 150.0, ), IconButton( icon: Icon(Icons.remove_circle), onPressed: ()=> print("to be implemented"), iconSize: 150.0, ), ], ) ], ), ); } void _addPressed() { //somehow call _updateSeconds() } }
And this is statefull MyTextWidget which I want to update.
class MyTextWidget extends StatefulWidget{ @override _MyTextWidgetState createState() => _MyTextWidgetState(); } class _MyTextWidgetState extends State<MyTextWidget> { int secondsToDisplay = 0; void _updateSeconds(int newSeconds) { setState(() { secondsToDisplay = newSeconds; }); } @override Widget build(BuildContext context) { return Text( secondsToDisplay.toString(), textScaleFactor: 5.0, ); } }
It seems like something quite simple what I want to achieve but I'm not able to figure it out. Imagine if "MyTextWidget" was buried in huge layout tree and every time I want to update it I would need to redraw whole tree again.
If you only want to call a function without any arguments, you can use the VoidCallback type instead of defining your own callback type. Calling a method of child widget from a parent widget is discouraged in Flutter. Instead, Flutter encourages you to pass down the state of a child as constructor parameters.
When you change the state of a Stateful Widget, use setState() to cause a rebuild of the widget and its descendants. You don't need to call setState() in the constructor or initState() of the widget, because build() will be run afterward anyway. Also don't call setState() in synchronous code inside build().
This is a possible solution using streams:
import 'dart:async'; import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Timer', theme: new ThemeData( primaryColor: Colors.grey.shade800, ), home: new MyHomePage(), ); } } class MyHomePage extends StatelessWidget { StreamController<int> _controller = StreamController<int>(); int _seconds = 1; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("title"), ), body: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ MyTextWidget(stream: _controller.stream), //just update this widget Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ IconButton( icon: Icon(Icons.add_circle), onPressed: _addPressed, iconSize: 150.0, ), IconButton( icon: Icon(Icons.remove_circle), onPressed: ()=> _controller.add(_seconds++), iconSize: 150.0, ), ], ) ], ), ); } void _addPressed() { //somehow call _updateSeconds() } } class MyTextWidget extends StatefulWidget{ final Stream<int> stream; MyTextWidget({this.stream}); @override _MyTextWidgetState createState() => _MyTextWidgetState(); } class _MyTextWidgetState extends State<MyTextWidget> { int secondsToDisplay = 0; void _updateSeconds(int newSeconds) { setState(() { secondsToDisplay = newSeconds; }); } @override void initState() { super.initState(); widget.stream.listen((seconds) { _updateSeconds(seconds); }); } @override Widget build(BuildContext context) { return Text( secondsToDisplay.toString(), textScaleFactor: 5.0, ); } }
There are different ways on how to achieve this. The really simple one is with InheritedWidget
widget, like that:
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Timer', theme: new ThemeData( primaryColor: Colors.grey.shade800, ), home: new MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override MyHomePageState createState() { return new MyHomePageState(); } } class MyHomePageState extends State<MyHomePage> { int _seconds = 1; @override Widget build(BuildContext context) { return new MyInheritedWidget( secondsToDisplay: _seconds, child: Scaffold( appBar: AppBar( title: Text("title"), ), body: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ MyTextWidget(), //just update this widget Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ IconButton( icon: Icon(Icons.add_circle), onPressed: _addPressed, iconSize: 150.0, ), IconButton( icon: Icon(Icons.remove_circle), onPressed: () => print("to be implemented"), iconSize: 150.0, ), ], ) ], ), ), ); } void _addPressed() { setState(() { _seconds++; }); } } class MyTextWidget extends StatelessWidget { @override Widget build(BuildContext context) { final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context); return Text(inheritedWidget.secondsToDisplay.toString(), textScaleFactor: 5.0, ); } } class MyInheritedWidget extends InheritedWidget { final int secondsToDisplay; MyInheritedWidget({ Key key, @required this.secondsToDisplay, @required Widget child, }) : super(key: key, child: child); static MyInheritedWidget of(BuildContext context) { return context.inheritFromWidgetOfExactType(MyInheritedWidget); } @override bool updateShouldNotify(MyInheritedWidget oldWidget) => secondsToDisplay != oldWidget.secondsToDisplay; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With