close

How to Set/Update State of StatefulWidget from other StatefulWidget in Flutter?

Kindly note that this article is outdated at this point. Indeed, it was my first little advance towards the states_rebuilder bundle.

Ripple is an exquisite, lightweight system for building cross-stage portable applications. It offers an extraordinary designer experience, particularly with regards to building UIs. On account of the a wide range of gadgets gave by the system, this undertaking is genuinely easy to do. With regards to state the executives, feelings separate. State the executives is probably the most smoking subject in the Flutter Community at the present time, with various strategies and perspectives.

Indeed, even the Flutter documentation states:

” This is an intricate point with solid, and varying, conclusions”

The result of any great state the executives framework is to isolate business rationale from the UI/Presentation one. There are many state the board strategies in the realm of Flutter with the most essential and simplest one being setState(fn) which is quickly plentiful for more intricate methods when managing more muddled applications. Jorge Coca has a decent article to assist you with comprehension and pick the correct state the board answer for your application.

In this article, I attempt to cause you to reevaluate setState(fn) and attempt to persuade you that it is all the more impressive then what it is was believed to be. What’s more, I will attempt to show that dealing with the state is the most straightforward piece of your shuddering time.

The primary thought is to pass the states just as the setState(fn) strategy to the BloC classes so one can utilize setState(fn) after state, transformation to remake any gadgets that one needs to be refreshed from the BloC.

To do as such, I will show the intensity of setState(fn) with the assistance of an average counter application that will have this gadgets pecking order:

Image for post
Widget hierarchy of the demo App. The app has three BloC classes, statefulWidgets (red rectangles) and statelessWidgets (white rectangles).

MainBloc is the highest BloC and it will be accessible to all gadgets of the application during the lifetime of the application. Here is the code of this class:

// File : ‘lib/blocs/main_bloc.dart’
import './bloc_setting.dart';
class MainBloc extends BloCSetting {
  final String title = "setState() is powerful";
  int counter1 = 0;
  int counter2 = 0;
}
MainBloc mainBloc; // do not instantiated it

The MainBloc class broadens a BloCSetting class that I will discuss it later. It characterizes a String field that holds the title of the application, likewise it characterizes two fields for following the estimations of counter one and two. In the last line, I pronounced a variable of type MainBloc. It is essential to not launch it at this stage.

The cout1Bloc is characterized as follows:

// File : ‘lib/blocs/count1_bloc.dart’
import 'bloc_setting.dart';
import 'main_bloc.dart';
class Count1Bloc extends BloCSetting {
  int counter1 = 0;
incrementCounter(state) {
    rebuildWidgets(
      setStates: () {
        counter1++;
      },
      states: [state],
    );
    mainBloc.counter1 = counter1;
  }
}
Count1Bloc count1Bloc;

Here I characterize a counter1 field and an incrementCounter strategy witch takes a contention of name state and of type dynamic (it closures to be State). The rebuildWidgets(this) is characterized in the class. The rebuildWidgets() job is to submit the state transformation and update the gadgets of the states that are recorded in the second contention of the strategy. I will speak later about the usage of this strategy.

This is the bloC part of the counter1 gadget. As should be obvious there is no reference to any UI parts neither to witch gadgets nor classes it will be utilized in.

We should move the UI beginning by the MyApp class:

// File : ‘lib/main.dart’
import 'package:flutter/material.dart';
import 'blocs/main_bloc.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    mainBloc = MainBloc();
  }
@override
  void dispose() {
    mainBloc = null;
    super.dispose();
  }
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "SetState management",
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

MyAppis a StatefulWidget type for the straightforward motivation to start up the mainBloc in the initState() strategy and demolish it in the arrange() technique to deliver assets and let it face its destiny with dart trash specialist. Presently on, any gadget underneath the MyApp gadget (all the application) has free admittance to the mainBloc.

for MyHomePage, there is not much. It characterizes tow catches to explore to counter a couple of pages.

// File : ‘lib/main.dart’
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(mainBloc.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text('Go To Counter 1'),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => Counter1(),
                  ),
                );
              },
            ),
            RaisedButton(
              child: Text('Go To Counter 2'),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => Counter2(),
                  ),
                );
              },
            )
          ],
        ),
      ),
    );
  }
}

The most intriguing part is in Counter1 definition:

// File : 'lib/counter1.dart'
import 'package:flutter/material.dart';
import 'blocs/main_bloc.dart';
import 'blocs/count1_bloc.dart';
class Counter1 extends StatefulWidget {
  @override
  _Counter1State createState() => _Counter1State();
}
class _Counter1State extends State<Counter1> {
  @override
  void initState() {
    super.initState();
    count1Bloc = Count1Bloc();
  }
@override
  void dispose() {
    count1Bloc = null;
    super.dispose();
  }
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(mainBloc.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button, on total, this many times:',
            ),
            Text(
              '${mainBloc.counter1}',
              style: Theme.of(context).textTheme.display1,
            ),
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${count1Bloc.counter1}',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count1Bloc.incrementCounter(this),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

It is a StatefulWidget that launches the count1Bloc in the initState() strategy and destructs it in the arrange() technique. The count1Bloc is supposed to be given to all the gadgets under the gadget where it is launched (Counter1 for our situation).

The title of the page is given from the mainBloc.title, the all out aggregate check of counter1 is taken structure mainBloc.counter1 and the real include is saved in count1Bloc.counter1.

The incrementCounter strategy is called with “this” as a contention, “this” here methods this condition of the statefulWidget.

Presently to see how the setState(fn) is passed to the BloC, how about we take a gander at the rebuildWidgets() in incrementCounter(state) strategy:

incrementCounter(state) {
    rebuildWidgets(
      setStates: () {
        counter1++;
      },
      states: [state],
    );
    mainBloc?.counter1 = counter1;
  }

The primary contention of rebuildWidgets() resembles the regular setState(fn) in ripple inside it state factors are transformed. The states contention is a rundown of states to be revamped as an outcome of this state change. I decide to pass a rundown to the states contention to make it accessible to me to remake numerous gadgets simultaneously.

The “this” in incrementCounter(this) is accessible in light of the fact that the FAB catch is inside a similar statefulWidget where I need it to be revamped. Consider the possibility that this isn’t the situation. For instance, we should move the FAB to another statelessWidget. Counter2 gadget is actualized in such a manner.

To start with, we should take a gander at Cout2Bloc:

// File : 'lib/blocs/count2_bloc.dart'import 'bloc_setting.dart';
import 'main_bloc.dart';class Count2Bloc extends BloCSetting {
var counter2State;
int counter2 = 0;incrementCounter() {
rebuildWidgets(
setStates: () {
counter2++;
},
states: [counter2State],
);
mainBloc?.counter2++;
}
}Count2Bloc count2bloc;

First I proclaimed the conter2State variable to hold the condition of counter2 which will be introduced from the counter2 gadget,

The remain is equivalent to in counter1 with the special case that “this” is taken out from the incrementCounter() contentions.

// File : 'lib/counter2.dart'import 'package:flutter/material.dart';
import 'blocs/main_bloc.dart';
import 'blocs/count2_bloc.dart';class Counter2Init extends StatefulWidget {
@override
_Counter2InitState createState() => _Counter2InitState();
}class _Counter2InitState extends State<Counter2Init> {
@override
void initState() {
super.initState();
count2Bloc = Count2Bloc();
}@override
void dispose() {
count2Bloc = null;
super.dispose();
}@override
Widget build(BuildContext context) {
return Counter2();
}
}

Of course, I launch and annihilate the count2Bloc in statefulWidget.

// File : 'lib/counter2.dart'class Counter2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(mainBloc.title),
),
body: Center(
child: Counter2Text(),
),
floatingActionButton: FloatingActionButton(
onPressed: count2Bloc.incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Counter2 is a statelessWidget and FAB calls the count2Bloc.incrementCounter with no contention.

Counter2Text is a statefulWidget that is utilized to show counter qualities:

// File : 'lib/counter2.dart'class Counter2Text extends StatefulWidget {
@override
_Counter2TextState createState() => _Counter2TextState();
}class _Counter2TextState extends State<Counter2Text> {
@override
void initState() {
super.initState();
count2Bloc.counter2State = this;
}@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button, on total, this many times:',
),
Text(
'${mainBloc.counter2}',
style: Theme.of(context).textTheme.display1,
),
Text(
'You have pushed the button this many times:',
),
Text(
'${count2Bloc.counter2}',
style: Theme.of(context).textTheme.display1,
),
],
);
}
}

The critical advance here happens inside initState() strategy. Here I instated the count2Bloc.counter2State to hold “this” state.

The rest of the class is extremely straightforward.

Return and see the BloC classes and comment they are liberated from any UI related segment. In a similar style, UI gadget classes are perfect of any BloC wreck. This permits me to pronounce a 100% partition of concerns.

It stays to me to show you how the BloCSetting class is actualized:

import 'package:flutter/material.dart';class BloCSetting extends State {
rebuildWidgets({VoidCallback setStates, List<State> states}) {
if (states != null) {
states.forEach((s) {
if (s != null && s.mounted) s.setState(setStates ??(){});
});
}
}@override
Widget build(BuildContext context) {
print(
"This build function will never be called. it has to be overriden here because State interface requires this");
return null;
}
}

BloCSetting stretches out State theoretical class to utilize ripple setState(fn) permitted. Inside it there is just on dynamic strategy rebuildWidgets() which takes a rundown of states, repeat among them and execute setState(fn) with the capacity gave in the setStates contention.

Image for post

That’s it. Hoping I convinced you of this technique. For myself, I am very convinced of the reliably of the technique and I used it to reproduce many of published state management demonstration apps such as:

1- Didier Boelens’ movie online catalog for Bloc;

2- Filip Hracek’s state experiments;

3- Brian Egan’s Flutter Architecture Samples;

4- All the showcases in the flutter State management documentation page;

Remarks:

1- For those how to do not like the way I provided the Blocs, they can provide them using the InheritedWidget class provider. (I would be very grateful if they show me why they prefer InheritedWidget method).

2- Adapting this approach, I found myself extensively using statefullWidget which pushes me to extend this approach to work with statelessWidget. How to do that is a seed of another possible article;

3- 1- I am not only constrained to use conventional variable types in my BloCs. I can use flutter predefined classes and enums. I also can conditionally style the UI from the bloc. For example, I can conditionally change the icon of FAB from the bloc.

This is the BloC part:

IconData counter1Icon = Icons.add;incrementCounter(state) {
counter1++;
counter1Icon = (counter1 < 10) ? Icons.add : Icons.add_circle;
mainBloc?.counter1 = counter1;

rebuildWidgets([state]);
}

Furthermore, the UI becomes:

floatingActionButton: FloatingActionButton(
        onPressed: () => count1Bloc.incrementCounter(this),
        tooltip: 'Increment',
        child: Icon(count1Bloc.counter1Icon),
      ),

4-rebuildWidgets can be called in the wake of anticipating async task and to refresh the UI appropriately. for instance:

incrementCounter(state) {
    counter1++;
    mainBloc?.counter1 = counter1;
    await Future.delayed(Duration(seconds: 1));
rebuildWidgets([state]);
  }

6-The aide class BloCSetting can be changed over to a library bundle or coordinated into the ripple default system to be accessible when bringing in the material library.

7-To work just with statelessWidgets, as I referenced in point 2 above, BloCSetting should be changed a tad.

8-you locate the entire application code in this Github repo:

Summery

It’s all About this issue. Hope all solution helped you a lot. Comment below Your thoughts and your queries. Also, Comment below which solution worked for you? Thank You.

Also Read

Leave a Comment