Flutter - New StatefullWidget, why related createState() method not called? - flutter

in the 'TestPageState', I put a button to call 'setState' method. new TestChildWidget() will create a new TestChildWidget,default construtor TestChildWidget() is called, but why 'createState()' method in TestChildWidget not called?
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new TestPage()
);
}
}
class TestPage extends StatefulWidget{
TestPageState createState(){
print('TestPageState createState');
return new TestPageState();
}
}
class TestPageState extends State<TestPage>{
#override
Widget build(BuildContext context) {
print('TestPageState Build');
// TODO: implement build
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new TestChildWidget(),
RaisedButton(
child: Text("刷新"),
onPressed: () => setState(() => print('setState')), //build method will called, new TestChildWidget() will be called
)
],
);
}
}
class TestChildWidget extends StatefulWidget{
TestChildWidget(){
//new TestChildWidget(), this default construtor will be called, but why createState() not called?
}
#override
State<StatefulWidget> createState() {
// TODO: implement createState
print('TestChildWidget createState');
return new TestChildWidgetState();
}
}
class TestChildWidgetState extends State<TestChildWidget>{
#override
Widget build(BuildContext context) {
print('TestChildWidgetState build');
// TODO: implement build
return Text('1111');
}
}
in the 'TestPageState', I put a button to call 'setState' method. new TestChildWidget() will create a new TestChildWidget,default construtor TestChildWidget() is called, but why 'createState()' method in TestChildWidget not called?

It creates a state or calls the state build when the state is created.
class TestChildWidgetState extends State<TestChildWidget>{
TestChildWidgetState(){
print("Created only once");
}
}
The framework calls createState whenever it inflates a StatefulWidget, which means that multiple State objects might be associated with the same StatefulWidget if that widget has been inserted into the tree in multiple places. Similarly, if a StatefulWidget is removed from the tree and later inserted in to the tree again, the framework will call createState again to create a fresh State object, simplifying the lifecycle of State objects.
See StatefulWidget

print(...) log maybe not print to console but Flutter have called createState()
You can try use:
developer.debugger(when: true);
in createState()

when setState() called, it will call
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
···
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
if canUpdate() return true, it means there is no need to create a new element.

The Stateful widget has a default parameter 'Key'. If you supply it with a different value each time - that would enforce new state to be created i.e createState() called.
Instead of new TestChildWidget(), try:
new TestChildWidget(
key: Key('different key for diff states'), // or key: UniqueKey()
),
I actually use the same key value for states that are same, in order to have the caching in those cases.

Related

Seperate steps in stepper into different files

I try to make each step will be in another dart file. (Steps contains a lot of code, it's hard to maintain it in single file)
What is the idea?
Create Scaffold with stepper (parent widget)
Parent widget creates an instance of steps, each instance modifies the same model (object of class)
Parent widget may call isValid() method on each step. (if it's valid, we can switch to next step)
Requirements:
Steps may be StatefulWidget or StatelessWidget (it may be hard to achieve so StatefulWidget will be enough)
Each step should contain bool isValid() method.
What I have? - Almost nothing
abstract class ValidatedStep {
bool isValid();
}
And see below Step, we implement this abstract class into Step1, but this method should ask state if it's valid. From StatefulWidget we do not have access to state.
class Step1 extends StatefulWidget implements ValidatedStep {
//its ok to create it here?
final _Step1State state = _Step1State();
#override
_Step1State createState() {
//we could (and should?) create state here, but variables in this widget should be final
return state;
}
#override
bool isValid() {
return state.isValid();
}
}
class _Step1State extends State<Step1> implements ValidatedStep {
#override
Widget build(BuildContext context) {
return Container();
}
#override
bool isValid() {
return true;
}
}
You can use map.. Here is a modified example of my code.. Perhaps you can modify to add function to validate the steps.
You can simply put the different Widget from different files in the list.
class StepperScreen extends StatelessWidget {
final List<SomeClass> list;
StepperScreen({#required this.list});
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(
'Stepper Screen',
),
),
body: Stepper(
steps: getStep(list),
),
),
);
}
}
List<Step> getStep(List<SomeClass> list) {
return list
.map(
(e) => Step(
title: Text(
e.name,
style: TextStyle(fontSize: 20),
),
content: e.child,
),
)
.toList();
}
Of course, your SomeClass should have the properties:
class SomeClass {
String name;
Widget child;
SomeClass({#required this.name, #required this.child});
}
A bit late to the party, but instead of asking the step if it isValid() pass some callback e.g. onReadyChange(bool) down to the step and call it from within the step according to your business logic.

missing concrete implementation of state.build

I am getting this error in Dart:
"Missing concrete implementation of "state.build""
The first method is the following:
class MyHomePage extends StatefulWidget {
// String titleInput;
// String amountInput;
#override
MyHomePageState createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
final List<Transaction> _userTransactions = [
// Transaction(
// id: "t1",
// title: "New Shoes",
// amount: 69.99,
// date: DateTime.now(),
// ),
// Transaction(
// id: "t2",
// title: "Weekly Groceries",
// amount: 16.53,
// date: DateTime.now(),
// ),
];
Does anyone knows what this error means and how to solve it?
Thank you.
You need to add a build method to the State of your widget, this method describes the part of the user interface represented by your widget, e.g.,
(add this to the MyHomePageState)
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child:
Container(
height: 200,
width: 100,
color: Colors.yellow,
),
),
);
}
Check the curly brace that closes the class where the Widget build method is found. You might just have closed it in the wrong place.
This was the case for me.
So make sure your build method is within the curly braces that enclose the class
class _LandingPageState extends State<LandingPage> {
Widget build(BuildContext context) {
...
}
All the Stateful widgets and Stateless widgets should have build method.
#override
Widget build(BuildContext context) {
return Container(
...
);
}
If you want to use it without build do not extend the class with State, use it like
class YourClassName {
}
Goto the definitation of State<T> class and see what are the abstract methods. You will find build() method as the only abstract method i.e. a method without body. So when yu are inheriting from State<MyHomePage>, you must override the build() and give a body; basically you will create your Widgets inside the build() method.
So to fix the error add the below code to your class:
class MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold( // Your Widget
...
);
}
}
Just check
#override
Widget build( Buildcontext context)
{
return Container();
}
Check spelling of build
reason : As u extend your class with stateless or statefull widget , U use overiding method to over ride the pre defined method which is already written in parent class which is
state less/full class

How can I access a public static variable from a different class in dart?

I am unable to access a public static boolean from a different class, eg. I have a boolean isFull in my StudyjiosListviewScreen class as shown:
class StudyjiosListviewScreen extends StatefulWidget {
#override
_StudyjiosListviewScreenState createState() => _StudyjiosListviewScreenState();
}
class _StudyjiosListviewScreenState extends State<StudyjiosListviewScreen> {
static bool isFull = false;
...
I want to use this boolean isFull in another class JoinStudyjio.
I created an instance of the StudyjiosListviewScreen class in the JoinStudyjio class like this:
StudyjiosListviewScreen listviewScreen = StudyjiosListviewScreen();
But when I try to use the boolean isFull like this:
if (listviewScreen.isFull) {
...
I get an error. I have already imported the file for the StudyjiosListviewScreen class inside the file for the JoinStudyjio class.
This is because StudyjiosListviewScreen and _StudyjiosListviewScreenState are 2 different classes.
The static variable isFull which you are trying to access is of the later one and you are trying to access it by creating an instance of the first one. If it had been a static variable of the class StudyjiosListviewScreen, you could have accessed it without even creating an instance of that class like this StudyjiosListviewScreen.isFull
If I understood your issue correctly, and following the suggestion I made in my comment, here is a code example of sharing a variable and a method to change it's value, down to two classes from a parent class:
class VariableSharing62951032 extends StatefulWidget {
#override
_VariableSharing62951032State createState() => _VariableSharing62951032State();
}
class _VariableSharing62951032State extends State<VariableSharing62951032> {
bool isFull = false;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ClassA62951032(isFull: isFull, swapIsFull: swapIsFull,),
ClassB62951032(isFull: isFull, swapIsFull: swapIsFull,),
],
);
}
void swapIsFull(){
setState(() {
isFull = !isFull;
});
}
}
class ClassA62951032 extends StatefulWidget {
final bool isFull;
final Function swapIsFull;
ClassA62951032({
this.isFull,
this.swapIsFull
});
#override
_ClassA62951032State createState() => _ClassA62951032State();
}
class _ClassA62951032State extends State<ClassA62951032> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Class A'),
Text(widget.isFull.toString()),
RaisedButton(
child: Text('Swap isFull'),
onPressed: () => widget.swapIsFull(),
),
],
);
}
}
class ClassB62951032 extends StatefulWidget {
final bool isFull;
final Function swapIsFull;
ClassB62951032({
this.isFull,
this.swapIsFull
});
#override
_ClassB62951032State createState() => _ClassB62951032State();
}
class _ClassB62951032State extends State<ClassB62951032> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Class B'),
Text(widget.isFull.toString()),
RaisedButton(
child: Text('Swap isFull'),
onPressed: () => widget.swapIsFull(),
),
],
);
}
}
Sharing variables and methods between classes it's a huge deal in Flutter.
First of all, you are passing it in the wrong way. That variable is saved in your state widget, which is defined as private.
So, or you define it as public and than you pass a key associated with your state, or you change complitelly approach. I don't like passing keys and it is not good for production, so I will give you a better example using providers:
add provider library to your pubspec.yaml:
provider: ^4.3.1 // Or latest version
Create a class where you can save that value:
class valuesHelper {
//In this class we are storing global, dynamic values
bool _isSeen;
valuesHelper() {
this._isSeen = false;
}
void setValue(bool value) {
this._isSeen = value;
}
bool getValue(){
return this._isSeen;
}
}
Now wrap your main with the provider and pass the valuesHelper();
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Provider(
create: (_) => valuesHelper(),
child: MaterialApp(
home: MyHomePage(),
),
);
}
}
Now call the Provider.of(context) wherever you want.
//Somwhere in your code when you have access to context:
ValueHelper helper = Provider.of<valueHelper>(context);
helper.setValue(true);
//Somwhereelse in your code when you have access to context:
ValueHelper helper = Provider.of<valueHelper>(context);
bool theValueIWant = helper.getValue();
If you have asynchronous stuff and huge state managment Blocs are even better and fancier, but for this kind of things Providers are more than enough.

How can I pass dynamic data from widget to widget?

Let me preface this by saying I am brand new to flutter/dart, and also not a super experienced programmer.
I'm trying to acquaint myself with flutter's framework and tools, and I'm trying to just expand upon the basic counter app that flutter creates on project generation. My goal is to have the app keep track of when the counter is 'reset', keep the time and count that the counter was at, and then display that data in a table on another screen.
Here's what I have so far:
I've made a class to keep track of the data:
class CounterRecord {
int _counter; //Holds the value the counter was at on reset
DateTime _resetTime; //Holds the time when the counter was reset
CounterRecord(int _count){
_counter = _count;
_resetTime = DateTime.now();
}
int getCount() => _counter; //fetch method for count
DateTime getTime() => _resetTime; //Fetch method for resettime
}
Here's the main class/home page:
import 'package:counter_app/clickerScreen.dart';
import 'package:counter_app/dataScreen.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
final clickerKey = new GlobalKey<ClickerScreenState>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.deepOrange,
accentColor: Colors.grey,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home'),
);
}
}
class MyHomePage extends StatefulWidget {
//Enables the passing in of the title, clicker screen instance, and datacreen isntance, respectively,
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
//We don't want a brand new clickerScreen every time, so I'm keeping it up here.
ClickerScreen clickerScreen = ClickerScreen(clickerKey: clickerKey); //Creates a new clickerScreen - the key points to it too.
#override
Widget build(BuildContext context) {
//Creates an instance (State?) of clickerScreen for the first tab
return DefaultTabController( //A wrapper that helps manage the tab states
length: 2, //Currently there are only two options for screens
child: Scaffold(
appBar: AppBar( //This represnts the bar up at the top
title: Text(widget.title),
bottom: TabBar(
tabs: [
//These are the icons for the two tabs we're using
//The order of these is important: It goes in the same order as TabBarView below
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.directions_run)),
],
)
),
body: TabBarView(
children: [
clickerScreen,
DataScreen( //this DataScreen will be built every time based on the new data from clickerScreen
data: clickerKey.currentState.getRecords(),
),
],
),
),
);
}
}
class CounterRecord {
int _counter; //Holds the value the counter was at on reset
DateTime _resetTime; //Holds the time when the counter was reset
CounterRecord(int _count){
_counter = _count;
_resetTime = DateTime.now();
}
int getCount() => _counter; //fetch method for count
DateTime getTime() => _resetTime; //Fetch method for resettime
}
Here's the important part of my clickerScreen file:
class ClickerScreen extends StatefulWidget {
ClickerScreen({Key clickerKey}) : super(key: clickerKey);
#override
ClickerScreenState createState(){
return ClickerScreenState();
}
}
class ClickerScreenState extends State<ClickerScreen> {
int _counter = 0;
List<CounterRecord> records;
/* All three of these functions do very similar things, modify the counter value. */
void _resetCounter(){
setState(() {
records.add(CounterRecord(_counter));
_counter = 0;
});
}
List<CounterRecord> getRecords(){
return records;
}
There is a build method in clickerScreen that just displays buttons and text. I'm not assigning the key in there, as it just returns a Center widget, but I've read some things that suggest maybe I should be.
And here is my dataScreen file:
import 'package:flutter/material.dart';
import 'main.dart';
class DataScreen extends StatefulWidget{
//Enables the passing in of the instance of the clicker screen instance
DataScreen({Key key, #required this.data}) : super(key: key);
final List<CounterRecord> data;
#override
_DataScreenState createState(){
return _DataScreenState();
}
}
class _DataScreenState extends State<DataScreen>{
#override
Widget build(BuildContext context) {
return Text(widget.data.toString());
}
}
I know that it the displaying won't actually look like it's supposed to, as I'm just sending it toString(), but I want to make sure I can pass the data in before I start messing around with that.
When I run this, I get a NoSuchMethod error on getRecords(), receiver: null. I've also tried to call createState() on the ClickerScreen widget, as a last-ditch attempt.
Any advice?
(I've pasted the entire clickerScreen file here (https://pastebin.com/j6Y8M8F3) since I didn't want to make this post any longer than it already is.)
If you have two widgets depending on the same state you have to use something called "lifting state up". That means that the state is part of the closest widget that has both other widgets as children. In your case that would be the MyHomePage Widget that holds the CounterRecord List. It passes the list through the constructer to the DataScreen, and passes the onReset callback to the ClickerScreen.
MyHomePage:
class MyHomePage extends StatefulWidget {
//Enables the passing in of the title, clicker screen instance, and datacreen isntance, respectively,
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<CounterRecord> counterRecord = []; //this is the lifted up state
onReset(int count) {
setState(() {
counterRecord.add(CounterRecord(count));
});
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
//A wrapper that helps manage the tab states
length: 2, //Currently there are only two options for screens
child: Scaffold(
appBar: AppBar(
//This represnts the bar up at the top
title: Text(widget.title),
bottom: TabBar(
tabs: [
//These are the icons for the two tabs we're using
//The order of these is important: It goes in the same order as TabBarView below
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.directions_run)),
],
)),
body: TabBarView(
children: [
ClickerScreen(onReset: onReset),
DataScreen(
data: counterRecord, //pass the record data to the datascreen
),
],
),
),
);
}
}
ClickerScreen:
class ClickerScreen extends StatefulWidget {
final Function(int) onReset;
ClickerScreen({Key clickerKey, this.onReset}) : super(key: clickerKey);
#override
ClickerScreenState createState() {
return ClickerScreenState();
}
}
class ClickerScreenState extends State<ClickerScreen> {
int _counter = 0;
/* All three of these functions do very similar things, modify the counter value. */
void _resetCounter() {
widget.onReset(_counter); //call the onReset callback with the counter
setState(() {
_counter = 0;
});
}
#override
Widget build(BuildContext context) {
....
}
}
DataScreen (can be Stateless, since state is in its parent)
class DataScreen extends StatelessWidget{
//Enables the passing in of the instance of the clicker screen instance
DataScreen({Key key, #required this.data}) : super(key: key);
final List<CounterRecord> data;
#override
Widget build(BuildContext context) {
return Text(widget.data.toString());
}
}
Using this simple approach can get very annoying fast, and needs lot of changes when you move a widget in the widget tree. Thats why advanced state management like Provider with ChangeNotifier or Bloc exist.
Here is a good read on this matter::
https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
I followed Leo Letto's advice and instead used an InheritedWidget placed at the very top that held a list of records.

What's the design benefit of Flutter's (Widget)State/StatefulWidget pattern?

My docs and Flutter videos, the explanation of the design of the StatefulWidget (+(Widget)State) is that it:
promotes a declarative design (good)
formalizes the process by which Flutter to efficiently decide which components need to be re-rendered (also good)
From the example:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {...}
}
However:
since we have to explicitly remember call setState in order to invalidate the state, is this really a declarative design?
Flutter doesn't automatically detect changes in the State object and decide to call build (although it could have), and so it doesn't really formalize/automate/make-safe the invalidation of view components. Since we have to explicitly call setState, what's the benefit of the Flutter's (Widget)State/StatefulWidget pattern over, let's say:
class MyHomePage extends StatefulWidget // Define dirty method
{
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
int _counter = 0;
_incrementCounter() {
_counter++;
this.dirty(); // Require the view to be rebuilt. Arranges generateView to be called.
}
#override
Widget generateView(BuildContext context) {return ... rendering description containing updated counter ... ;}
}
... which would place the same burden of marking the UI dirty on the programmer, is no less decalrative, and avoids additional abstraction that obfuscates the intention of the program.
What have I missed? What's the benefit of separating of StatefulWidget from (Widget)State in Flutter?
[Before people chime in with MVC comments, note that the Flutter model rather explicitly only manages only the widget's state and its tightly coupled to the UI's Widget through the build method - there is no separation of concern here and it doesn't have a lot to say about larger application state that's not attached to a view.]
[Also, moderators, these not the same questions: Why does Flutter State object require a Widget?, What is the relation between stateful and stateless widgets in Flutter?. My question is one about what's the benefit of the present design, not how this design works.]
Update: #Rémi Rousselet -- Here's a declarative example with only a new state class needing to be declared. With some work, you could even get rid of that (though it may not be better).
This way of declaring interaction with need didn't require (the user) declaring two new circularly type-referencing class, and the widget that is changing in response to state is decoupled from the state (its constructed a pure function of the state and does not need to allocate the state).
This way of doing things doesn't survive hot-reload. (sad face).
I suspect this is more of an issue with hot-reload, but if there's a way to make it work it would be great,
import 'dart:collection';
import 'package:flutter/material.dart';
////////////////////////////////
// Define some application state
class MyAppState with ChangeSubscribeable<MyAppState> {
/***
* TODO. Automate notifyListeners on setter.
* Binds changes to the widget
*/
int _counter;
get counter => _counter;
set counter(int c) {
_counter = c;
notifyListeners(); // <<<<<< ! Calls ... .setState to invalidate widget
}
increment() {
counter = _counter + 1;
}
MyAppState({int counter: 0}) {
_counter = counter;
}
}
void main() => runApp(MyApp5());
class MyApp5 extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Declare the mutable state.
// Note because the state is not coupled to any particular widget
// its possible to easily share the state between concerned.
// StateListeningWidgets register for, and are notified on changes to
// the state.
var state = new MyAppState(counter: 5);
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: Center(
child: Column(
children: [
// When the button is click, increment the state
RaisedButton(
onPressed: () => {
state.increment(),
print("Clicked. New state: ${state.counter}")
},
child: Text('Click me'),
),
// Listens for changes in state.
StateListeningWidget(
state,
// Construct the actual widget based on the current state
// A pure function of the state.
// However, is seems closures are not hot-reload.
(context, s) => new Text("Counter4 : ${s.counter}"),
),
],
))),
);
}
}
// //////////////////////
// Implementation
// This one is the onChange callback should accept the state.
//typedef OnChangeFunc<ARG0> = void Function(ARG0);
typedef OnChangeFunc = void Function();
mixin ChangeSubscribeable<STATE> {
final _listener2Notifier =
new LinkedHashMap<Object, OnChangeFunc>(); // VoidFunc1<STATE>>();
List<OnChangeFunc> get _listeners => List.from(_listener2Notifier.values);
void onChange(listenerKey, OnChangeFunc onChange) {
// onChange(listenerKey, VoidFunc1<STATE> onChange) {
assert(!_listener2Notifier.containsKey(listenerKey));
_listener2Notifier[listenerKey] = onChange;
print("Num listeners: ${_listener2Notifier.length}");
}
void removeOnChange(listenerKey) {
if (_listener2Notifier.containsKey(listenerKey)) {
_listener2Notifier.remove(listenerKey);
}
}
void notifyListeners() {
// _listener2Notifier.forEach((key, value)=>value(state));
// Safer, in-case state-update triggers add/remove onChange:
// Call listener
_listeners.forEach((value) => value());
}
}
typedef StateToWidgetFunction<WIDGET extends Widget,
STATE extends ChangeSubscribeable>
= WIDGET Function(BuildContext, STATE);
void noOp() {}
class _WidgetFromStateImpl<WIDGET extends Widget,
STATE extends ChangeSubscribeable> extends State<StatefulWidget> {
STATE _state;
// TODO. Make Widget return type more specific.
StateToWidgetFunction<WIDGET, STATE> stateToWidgetFunc;
_WidgetFromStateImpl(this.stateToWidgetFunc, this._state) {
updateState(){setState(() {});}
this._state.onChange(this, updateState);
}
#override
Widget build(BuildContext context) => stateToWidgetFunc(context, this._state);
#override
dispose() {
_state.removeOnChange(this);
super.dispose();
}
}
class StateListeningWidget<WIDGET extends Widget,
STATE extends ChangeSubscribeable> extends StatefulWidget {
STATE _watched_state;
StateToWidgetFunction<WIDGET, STATE> stateToWidgetFunc;
StateListeningWidget(this._watched_state, this.stateToWidgetFunc) {}
#override
State<StatefulWidget> createState() {
return new _WidgetFromStateImpl<WIDGET, STATE>(
stateToWidgetFunc, _watched_state);
}
}
I've been directed at the ChangeProvider pattern: https://github.com/flutter/samples/blob/master/provider_counter/lib/main.dart
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Flutter Demo Home Page'),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<Counter>( // <<< Pure. Hidden magic mutable parameter
builder: (context, counter, child) => Text(
'${counter.value}',
style: Theme.of(context).textTheme.display1,
),),],),),
floatingActionButton: FloatingActionButton(
onPressed: () =>
// <<< Also a hidden magic parameter
Provider.of<Counter>(context, listen: false).increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
... but this also suffers problems:
its not clear to reader of what the state requirements are or how to provide them -- the interface (at least in this github example HomePage) example does not require Counter as a formal parameter. Here we have new HomePage() that has configuration that is not provided in its parameters - this type of access suffers similar problems to global variables.
access to state is by class type, not object reference - so its not clear (or at least straightforward) what to do if you want two objects of the same type (e.g. shippingAddress, billingAddress) that are peers in the model. To resolve this, the state model likely needs to be refactored.
I think I'm with user48956 on this. (Catchy name by the way).
Unfortunately, the Flutter authors seem to have suffixed their View class with the word 'State'. This has rather confused the whole Flutter state management discussions.
I think the purpose of the two classes is actually to make the painting more performant but it comes with a very heavy plumbing cost for us developers.
As to the naming convention:
The dirty flag approach allows the widget painter to optimise their painting without knowing about our state, thereby alleviation the need for two classes.
Also generateView() is kinda meaningful (unless of course, you start using these widgets to hold model-fragments (as per Package:provider).