Flutter interaction with change in memory - flutter

I am trying to understand how a flutter Widget populated by a memory item behaves when the underlying memory item changes. My use case is that: by trigger of some event(using EventBus package), this underlying memory item may or may not change. If it does, then I would like to trigger a setState(), but not otherwise as I wouldn't want to unnecessarily call it. Kindly help me as to how I can achieve this.Currently the onclick changes the str but the Text widget doesn't change. My example -
button click(acting as event trigger) changes underlying str item that populates the Text item. Thanks in advance.
class _MainApp extends State<MainApp>{
#override
String str = "1";
Widget build(BuildContext context){
return MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
CustomText(),
RaisedButton(onPressed: (){
print(CustomTextS.str);
CustomTextS.setStr("f");
print(CustomTextS.str);
},)
],
),
),
);
}
}
class CustomText extends StatefulWidget{
CustomTextS createState() => CustomTextS();
}
class CustomTextS extends State<CustomText>{
static String str = "1";
static setStr(String a){
str = a;
}
Widget build(BuildContext context){
return Text(str);
}
}

Currently I proceeded to use the fact that Strings and integers are passed by value. Thus one can use that fact to get effectively a copy which one can use to compare with the updated value to determine whether or not on event action a widget's properties have changed or not.

Related

Best way to pass widgets to child widget in flutter

I'm new to flutter but I have a widget that wraps a custom painter. I am trying to get it to work so I can supply a Widget to this child widget's constructor and then use that widget as the child of the custom painter.
For example:
class MyPainterWrapper extends StatefulWidget {
Widget _childWidget;
SceneRender([this._childWidget]);
#override
State<StatefulWidget> createState() {
return new MyPainterWrapperState(_childWidget);
}
}
class MyPainterWrapperState extends State<SceneRender> {
Widget _childWidget;
MyPainterWrapperState(this._childWidget);
#override
Widget build(BuildContext context) {
return Column(
children: [
CustomPaint(painter: MyPainter(), child: _childWidget)
],
);
}
}
And in another widget (called testWidget):
bool _answerCorrect = false;
bool _answerInputted = false;
var _msgController = TextEditingController();
FocusNode _answerFieldFocus = new FocusNode();
DictionaryEntry _currentEntry;
void _checkIfCorrect(String answerGiven) {
setState(() {
_answerCorrect = false;
if (_currentEntry.Full_Word == answerGiven)
_answerCorrect = true;
else if (_currentEntry.dictReadings.isNotEmpty) {
for (AlternateDictionaryEntryReading entryReading in _currentEntry
.dictReadings) {
if (entryReading.Alternate_Reading == answerGiven) {
_answerCorrect = true;
break;
}
}
}
_answerInputted = true;
_msgController.clear();
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: MyPainterWrapper(Center(Container(Column(children: <Widget>[
if (_answerCorrect && _answerInputted) Text('CORRECT!'),
if (!_answerCorrect && _answerInputted) Text('WRONG:'),
if (_answerInputted)
Text(_currentEntry.Full_Word),
if (_answerInputted)
for(AlternateDictionaryEntryReading reading in _currentEntry.dictReadings)
Text(reading.Alternate_Reading),
Container(
constraints: BoxConstraints.expand(
height: 100,
width: 1000
),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (DictionaryTranslation translation in _currentEntry.dictTranslations)
Text(translation.Translation),
],
)
),
),
Text('Enter Answer:',),
TextField(
controller: _msgController,
focusNode: _answerFieldFocus,
onSubmitted: (String value) {
_checkIfCorrect(value);
_answerFieldFocus.requestFocus();
},
)
This works to render the first time correctly, but any setState calls from checkIfCorrect from testWidget do not force the child widget to rebuild. I've tried testing it this way and it works, so that leads me to believe that I'm passing the widget incorrectly to have it redrawn via setState
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: CustomPaint(painter: TestPainter(), child: Center(
child: Container(...))
Your MyPainterWrapperState class reads like you are creating a new _childWidget property inside your state (which has a default value of null). You are then using it to initialize a new instance of MyPainterWrapperState, then throwing away the instance of MyPainterWrapper() that you just created.
You're not actually using the stateful part of your stateful widget at all; You're just calling a method that returns something once.
That said, your approach is basically right, but the implementation is off a little.
My advice:
First, you can use named properties to supply constructor arguments to your class. I've made that change in the code snippet shown below.
The State class of a stateful widget supplies a widget property that should be used to reference the properties of the widget that created it. The State widget should also have a solitary initializer that accepts no arguments.
Also good to know is that the State class provides an initState() method that you can override to initialize any class local variables that you declare. This should be used to give a value to your _childWidget property.
Finally, anything you expect to be rebuilt should be inside the MyPainterWrapperState() class. Your SceneRender() method doesn't appear in your code, but you might want to move it into MyPainterWrapperState() if you expect the scene to change based on the value of the child.
I suggest these changes.
Pass arguments to MyPainterWrapper via named arguments.
Remove the argument to MyPainterWrapperState() and reference the child through the widget property supplied to the state.
Initialize _childWidget by overriding initState()
If SceneRender() does anything that depends on the value of _childWidget, move it to the build() method of MyPainterWrapperState().
The Flutter docs are great, and the Flutter team has created a ton of helpful YouTube videos that explain in a couple of minutes examples of how to use dozens of them. For a better understanding of StatefulWidget, you can read about it here.
If you make these changes, your solution would look something like the code snippet below.
Presuming you make those changes, you would alter your call to MyPainterWrapper() to use named properties.
Change this line
body: MyPainterWrapper(Center(Container(Column(children: <Widget>[
To this
body: MyPainterWrapper(child: Center(Container(Column(children: <Widget>[
This won't get you to done, but it will get you closer. I also haven't run this through a compiler, so there are probably errors in the snippet, but this should serve to illustrate the approach.
class MyPainterWrapper extends StatefulWidget {
MyPainterWrapper(
{
#required child: this.childWidget,
}
);
final Widget childWidget;
// Not sure what this does, but I'm pretty sure that it doesn't
// provide anything into the widget tree.
// If it mutates its arguments, then you might still need it.
// SceneRender([this._childWidget]);
#override
State<StatefulWidget> createState() {
// Note no arguments.
return new MyPainterWrapperState();
}
}
class MyPainterWrapperState extends State<MyPainterWrapper> {
// This is an uninitialized variable inside this class.
Widget _childWidget;
// MyPainterWrapperState(this._childWidget);
// Initialize state variables here.
#override initState() {
// Assigns the widget class initializer to your state variable.
_childWidget = widget.childWidget;
}
#override
Widget build(BuildContext context) {
return Column(
children: [
CustomPaint(painter: MyPainter(), child: _childWidget)
],
);
}
}```

Flutter-getx, widget does not re-render when RxMap set to a new value

I'm building a widget that renders based on the contents of an RxMap. I start with the RxMap initialized to empty, and upon clicking a button set the RxMap to contain a new value. After setting the new value for the RxMap, the widget does not re-render to display the new map values.
Here's my code:
(EDIT: included all the boilerplate as well to prevent confusion. Each class is in a separate file with relevant imports in all the other files.)
class MyController extends GetxController {
var selectedItem = {}.obs;
}
class MyBinding extends Bindings {
#override
void dependencies() {
Get.put(MyController());
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext highContext) {
return GetMaterialApp(
title: 'App',
getPages: [
GetPage(
name: "/test",
page: () => MyClass(),
binding: MyBinding()),
],
initialRoute: "/test",
);
}
}
final saController = Get.find<MyController>();
class MyClass extends StatelessWidget {
Widget build(context) {
print(saController.selectedItem);
return Scaffold(
child: Column(
children: [
Obx(() => Container(
child: Text(saController.selectedItem.toString())
)),
TextButton(
child: Text("click"),
onPressed: (() {
saController.selectedItem = RxMap({"test": "item"});
saController.selectedItem.refresh();
saController.update();
print(saController.selectedItem);
})
)
]
)
);
}
}
The first print statement correctly prints the empty object to the console and the onPressed print statement correctly prints the new object with its key and value included to the console, but the widget does not re-render with the new value. refresh() and update() don't appear to do anything.
This seems to be specific to RxMap, I've been able to get other datatypes to update on change successfully, e.g. swapping out the RxMap value for a boolean value and toggling it from true to false on click causes the widget to re-render correctly without changing any other parts of the code.
Your UI will not trigger an update unless the value of a Rx variable (selectedItem.value) is changed. But you are updating the Rx variable (selectedItem) itself.
Therefore,you need to change saController.selectedItem = RxMap({"test": "item"}); to saController.selectedItem.value = {"test": "item"};
And I think you don't actually need to call refresh() and update() anymore.

what does it mean when we see people calling widget in dart?

I have seen many times people calling widget. sth inside the code.
May I know what it is actually doing?
For example code below, (highlighted part is my confusion)
class _MyOwnClassState extends State<MyOwnClass> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: makeWidgetChildren(**widget.jsonObject)**),
),
);
}
}
In flutter's StatefulWidget, we have the following architecture.
You have a StatefulWidget like this,
class MyOwnClass extends StatefulWidget {
State createState () => _MyOwnClassState();
}
And you have a State class for your StatefulWidget like this,
class _MyOwnClassState extends State<MyOwnClass> {
}
Now, State class is meant to house variables that tend to change in order for your UI to be rebuilt.
So you can have variables in your State that you can update using setState.
But what if you had some data that doesn't change and you want to avoid putting them inside the State class.
That's where your StatefulWidget comes to play.
You can store variables in your MyOwnClass and the widget variable inside the State class gives you a way to access them.
For example,
class MyOwnClass extends StatefulWidget {
int numberThatDoesntChange = 1;
State createState () => _MyOwnClassState();
}
You can access them in your State class like this,
class _MyOwnClassState extends State<MyOwnClass> {
Widget build(BuildContext context) {
return Text('$widget.numberThatDoesntChange');
}
}
Apart from this, your StatefulWidget has many more internal instance members that you can access inside of your State class using the widget variable.
The widget refers to the actual view that renders on the screen. It extends the StatefulWidget class of the flutter framework and overrides the createState() method. The createState() method is used to create the instance of state class. We will look into createState().
The state class is used to maintain the state of the widget so that it can be rebuilt again. It extends the State class of the flutter framework and overrides the build method.
The framework calls build() method again and again whenever setState() method is called. The setState() method notifies the framework that the internal state of this object has changed and it should be rebuilt. Suppose we change the value of text in StatefulWidget then we need to call setState().
Edit As Nisanth pointed outh in his comment - I missed your question completely; please ignore the below....
Let me try my answer, I don't think others are getting your point.
In your exapmle, Column(children: x) expect a list of Widgets.
You have two options - either provide this list directly:
class _MyOwnClassState extends State<MyOwnClass> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: <Widget>[SomeWidget()]),
),
);
}
}
Or if you have more complex code that generates widget - based on input parameters, or you have the same widget generated multiple times and you want to avoid the code duplication - you would create the separate function to do the job.
Something like:
class _MyOwnClassState extends State<MyOwnClass> {
List<Widget> makeWidgetChildren(int param) {
/*
some very complex logic here
/*
if (param>3 && param<4) {
return List<Widget>.generate(4, (index)=>SomeWidget1(index));
} else {
return <Widget>[Center()];
}
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: makeWidgetChildren(**widget.jsonObject)**),
),
);
}
}
So basically, it is just to make the code nicer; and to avoid having code repeated over and over again in the build function.

Separate widgets in other files flutter

I want to make my code neater but I have a problem when I separate widgets that I use often in 1 file
here is it my main widget
import 'package:a_tiket/Helpers/widget_helper.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool _isLoading = false;
var _message = '';
var _hasError = false;
#override
Widget build(BuildContext context) {
return
_isLoading ?
_loadingWidget(context)
:
Scaffold(
body: SingleChildScrollView(
child: Container(
),
],
),
)
)
)
;
}
}
this is my widget_helper.dart
Widget _loadingWidget (BuildContext context){
return Scaffold(
body: Center(
child: CircularProgressIndicator(
backgroundColor: ACCENT_COLOR,
valueColor: new AlwaysStoppedAnimation<Color>(PRIMARY_COLOR),
),
),
);
}
the problem is i got some error. i have add import for widget_helper but still get error
lib/Pages/loginPage.dart:193:7: Error: The method '_loadingWidget' isn't defined for the class '_LoginPageState'.
what should i do? i just want to make the code neater
please remove underline
change from
_loadingWidget(context)
to
loadingWidget(context)
There are a few issues with your code:
For such a small piece of code like showing a
CircularProgressIndicator you should not be putting a method in a separate
file. Instead of making your code "neater", you are making it harder
to read. If you really want to have it in a separate file, create a Stateless widget that shows the code you want. But then again you are just using a CircularProgressIndicator. You aren't saving any code, just creating more unnecessary code.
You already have a Scaffold where your are going to show the CircularProgressIndicator. You don't need to have another one. It's not doing anything.
While Dart uses camelCase for variable naming, file names use snake_case. Try to use it when naming files.

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