Widget not updating flutter - flutter

I'm trying to change the variable from another stateful class.
class first extends statefulwidget {
bool text = false;
Widget build(BuildContext context) {
setState((){});
return Container(
child: text ? Text('Hello') : Text('check')
);
}
}
class second extends statefulwidget {
Widget build(BuildContext context) {
return Container(
child: IconButton(
onPressed: () {
first fir = first();
setState((){
fir.test = true;
});
}
)
);
}
}
widget shows only check not showing Hello
This is my code...Ignore spelling mistakes and camelcase
Give me the solutions if you know..

If you are trying to access data on multiple screens, the Provider package could help you. It stores global data accessible from all classes, without the need of creating constructors. It's good for big apps.
Here are some steps to use it (there is also a lot of info online):
Import provider in pubspec.yaml
Create your provider.dart file. For example:
class HeroInfo with ChangeNotifier{
String _hero = 'Ironman'
get hero {
return _hero;
}
set hero (String heroName) {
_hero = heroName;
notifyListeners();
}
}
Wrap your MaterialApp (probably on main.dart) with ChangeNotifierProvider.
return ChangeNotifierProvider(
builder: (context) => HeroInfo(),
child: MaterialApp(...),
);
Use it on your application! Call the provider inside any build method and get data:
#override
Widget build(BuildContext context){
final heroProvider = Provider.of<HeroInfo>(context);
return Column {
children: [
Text(heroProvider.hero)
]
}
}
Or set data:
heroProvider.hero = 'Superman';

try to reference to this answer, create function to set boolean in class1 and pass as parameter to class 2 and execute it :
typedef void MyCallback(int foo);
class MyClass {
void doSomething(int i){
}
MyOtherClass myOtherClass = new MyOtherClass(doSomething);
}
class MyOtherClass {
final MyCallback callback;
MyOtherClass(this.callback);
}

Related

Riverpod: List provider is not rebuilding

Flutter riverpod is not notifying the Consumer on the state change when the StateNotifier's type is List, while the same implementation works just fine for other types.
here, I provided a minimal reproducable example:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
home: MyHomePage(),
),
);
}
}
class CounterState extends StateNotifier<List<int>> {
static final provider = StateProvider(
(ref) => CounterState(),
);
int get last {
print('last');
return state.last;
}
int get length {
print('len');
return state.length;
}
// the body of this will be provided below
add(int p) {}
CounterState() : super(<int>[0]);
}
class MyHomePage extends ConsumerWidget {
#override
Widget build(BuildContext context, watch) {
void _incrementCounter() {
final _count = Random.secure().nextInt(100);
context.read(CounterState.provider.notifier).state.add(_count);
}
var count = watch(CounterState.provider.notifier).state.length;
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text(
'You have pushed the button this many times: $count',
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: Icon(Icons.add),
),
);
}
}
as for the add method, I tried implementing it in a lot of ways, but neither works.
here is what I tried:
1: just add it straight away:
add(int p) {
state.add(p);
}
2: I also tried the solution suggested in this answer:
add(int p) {
state = [...state, p];
}
3: I tried to destroy the list entirely, and reassign it:
add(int p) {
final _state = [];
// copy [state] to [_state]
for (var item in state) {
_state.add(item);
}
// empty the state
state = [];
// add the new element
_state.add(p);
// refill [state] from [_state]
for (var item in _state) {
state.add(item);
}
print(state.length); // it continues until here and prints
}
Firstly, you are not creating the correct provider to listen to a StateNotifier. You need to change this:
static final provider = StateProvider(
(ref) => CounterState(),
);
to this:
static final provider = StateNotifierProvider<CounterState, List<int>>(
(ref) => CounterState(),
);
Please refer to the Riverpod documentation about the different types of providers.
Secondly, you are not actually watching for state changes, but you are just getting the state object from the notifier.
Change this line:
var count = watch(CounterState.provider.notifier).state.length;
to this:
final count = watch(CounterState.provider).length;
also, your increment method is not correct for StateNotifier providers. Please change this:
context.read(CounterState.provider.notifier).state.add(_count);
to this:
context.read(CounterState.provider.notifier).add(_count);
It should rebuild now when the state changes. However, you do need an implementation of your add method that actually changes the state object itself. I would suggest the second variant you mentioned, that is in my opinion the nicest way to do this:
add(int p) {
state = [...state, p];
}
#TmKVU explained well, so I'm skipping that part. You can also follow riverpod document.
here is my example of riverPod:
stateNotifierProvider
stateProvider
Your widget
import 'dart:math';
import 'package:stack_overflow/exports.dart';
class CounterState extends StateNotifier<List<int>> {
static final provider = StateNotifierProvider(
(ref) => CounterState(),
);
int get last {
print('last');
return state.last;
}
int get length {
print('len');
return state.length;
}
// the body of this will be provided below
add(int p) {}
CounterState() : super(<int>[0]);
}
class MyHomePageSSSS extends ConsumerWidget {
#override
Widget build(BuildContext context, watch) {
void _incrementCounter() {
final _count = Random.secure().nextInt(100);
context.read(CounterState.provider.notifier).state =
context.read(CounterState.provider.notifier).state..add(_count);
}
final countprovider = watch(CounterState.provider);
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text(
'You have pushed the button this many times: ${countprovider.length}',
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: Icon(Icons.add),
),
);
}
}

Can you use a class name in a conditional statement?

I have a custom widget that uses a ListTile. I would like to set the Leading: property to a Checkbox if the Class A is building the widget, but set the Leading property to Null if Class B is building the widget.
Is it possible for the ListTile to know the name of the class that is building it?
Or is there a better way to approach this type of problem?
You can either use the is operator or use obj.runtimeType to check the type of object.
Refer to this link to understand the difference between them.
Here's an example snippet.
class CustomListTile{
var obj;
CustomListTile(this.obj);
void isSameClass(){
// if(obj.runtimeType == Truck)
if(obj is Truck){
print("Building checkbox");
}else{
print("returning Null");
}
}
}
class Chopper{
void test(){
CustomListTile obj = CustomListTile(this);
obj.isSameClass();
}
}
class Truck{
void test(){
CustomListTile obj = CustomListTile(this);
obj.isSameClass();
}
}
void main(){
Chopper objChop = Chopper();
objChop.test();
Truck objTruck = Truck();
objTruck.test();
}
Would passing a boolean like this do the job for you?
class CustomListTile extends StatelessWidget {
const CustomListTile({Key? key, this.hasLeading = false}) : super(key: key);
final bool hasLeading;
#override
Widget build(BuildContext context) {
return ListTile(
leading: hasLeading ? const Icon(Icons.person) : null,
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: [
CustomListTile(hasLeading: true), // This one has leading
CustomListTile(), // This one does not
],
);
}
}

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.

Flutter redux: StoreConnector vs StoreProvider

I've been using flutter_redux for only very few days and I'm wondering what's the difference between:
class BtnCustom extends StatelessWidget {
#override
Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
return FlatButton(
onPressed: store.dispatch(MyCustomAction),
child: Text(store.state.MyCustomTxt),
);
}
}
and
class BtnCustom extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _ViewModel>(
converter: (store) => _ViewModel(
txt: store.state.MyCustomTxt,
onPressed: store.dispatch(MyCustomAction)),
builder: (BuildContext context, _ViewModel vm) {
return FlatButton(
onPressed: vm.onPressed,
child: Text(vm.txt),
);
},
);
}
}
class _ViewModel {
final String txt;
final void Function() onPressed;
_ViewModel({this.txt, this.onPressed});
}
?
The first one seems so handy to use. Are they any advantages or drawbacks of using one over another I should be aware of?
According to the documentation, the StoreConnector will rebuild the widget in it so:
Is it ok to not use a StoreConnector when you don't need to rebuild a widget?
Is it ok to have multiple widgets within a StoreConnector?
StoreConnector gives you more control over widget, especially when you don't want to rebuild it. StoreConnector:
lets you detect whether the ViewModel has changed (whether widget should be rebuilt) if you use distinct: true and override hashCode and == in your ViewModel;
lets you skip widget rebuild altogether by quickly checking some specific store.state:
StoreConnector<AppState, MyViewModel>(
distinct: true,
ignoreChange: (state) {
return state.someVariable == theValueIDontCareAbout;
},
...
),
class MyViewModel{
#override
bool operator ==(other) {
return (other is MyViewModel) && (this.someVmVariable == other.someVmVariable);
}
#override
int get hashCode {
int result = 17;
result = 37 * result + someVmVariable.hashCode;
return result;
}
}
And some more fine-grained controls. Take a look at the documentation of StoreConnector's constructor. If multiple widgets inside store sonnector share the same ViewModel it is natural to have them like that. However, if it's possible to prevent them from rebulding by separating their ViewModels you can use separate StoreConnectors.

How to pass setstate as a parameter to a class method

in flutter I use a class to load values for switch widgets from a database and then update that database when the switch is toggled. Somehow I need to have that class call setstate on the calling function of the instance but it doesn't seem to work.
See the code below for an example.
The first switch is how I'd write it without the database class. That is working fine, when tapping the switch it both moves and the print shows that the value changed.
In the second switch widget however, I used the database class to build it but it doesn't seem to call the callback function correctly. The print always prints false.
I thought I tried all combinations of => and (){} but something is still amiss. I'm pretty sure the problem is how the callback is handled in the line: callBackFunctionForSetState();
maybe that should be called with callBackFunctionForSetState((){}); but that also doesn't work.
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
bool myBool = true;
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
home: ScreenUpgrades(),
);
}
}
class ScreenUpgrades extends StatefulWidget {
#override
_ScreenUpgradesState createState() => _ScreenUpgradesState();
}
class _ScreenUpgradesState extends State<ScreenUpgrades> {
#override
Widget build(BuildContext ctxt) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Upgrades"),
),
body: FutureBuilder(
future: buildSwitchList(),
builder: (BuildContext ctxt, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView(children: snapshot.data);
} else {
return Center(child: CircularProgressIndicator());
}
}));
}
Future<List> buildSwitchList() async {
List<Widget> widgetList = [];
//This one below for a working example only
widgetList.add(Switch(value: myBool,onChanged: (bb)=>nonDBSetState()));
//Normally I'll create a bunch of widgets by loading their data from the DB as below
widgetList.add(DataBaseSwitchBuilder(1,()=>setState((){})).listViewWidget);
return widgetList;
}
nonDBSetState()
{
myBool = !myBool;
print('New value of first switch: ' + myBool.toString());
setState((){});
}
}
class DataBaseSwitchBuilder {
Widget listViewWidget;
int dbID;
bool onOff;
Function callBackFunctionForSetState;
DataBaseSwitchBuilder (int paramID, Function callBack)
{
dbID=paramID; //used to query the parameter from the DB
onOff = true;
callBackFunctionForSetState=callBack;
listViewWidget=(Switch(value: onOff,onChanged: (bb)=> updateDBAndState()));
}
updateDBAndState()
{
//update the switch
onOff = !onOff;
print('DB Swtich value now: ' + onOff.toString());
//first we save the record in the DB
//todo: code for updating DB
//Then call the passed function which should be a setstate from the calling function
//Below doesn't seem to work.
callBackFunctionForSetState();
}
}
I'm just expecting that the updateDBAndState will allow me to save the new value of the switch to the database and then call the setstate callback.
Just to respond to "How to pass setstate as a parameter to a class method"
widget controler
class Controller {
Controller._privateConstructor();
factory Controller() => _singleton;
static final Controller _singleton =
Controller._privateConstructor();
late Function setStateHandler;
void initSetState(Function setState) => setStateHandler = setState;
void triggerSetState() => setStateHandler();
}
widget
#override
void initState() {
super.initState();
controller.initSetState(() => setState(() {
widgetVariable = true;
}));
}