'Flutter redux combined state dispatch action and update UI issue
I am learning redux. I have the following issues:
- My initial state does not seem to be taken into the account
- My actions are dispatched but they are not updating the actual state on the UI
I am trying to understand how things work so please be patient with me. I will be grateful to anyone that can help with this as I tried numerous things and I think I am stuck at this point. Thank you for all your kind help and comments.
main.dart file:
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
//Actions for redux, these are the things we do
enum FirstActions { Increment, Decrement, Reset }
enum SecondActions { Increment, Decrement, Reset }
//The reducer will take the action and create a state
// TODO: 5. update state values
dynamic firstReducer(dynamic state, dynamic action) {
print('first ${state}');
switch (action) {
case FirstActions.Increment:
state++;
break;
case FirstActions.Decrement:
state--;
break;
case FirstActions.Reset:
state = 0;
break;
}
print('first ${state}');
return state;
}
dynamic secondReducer(dynamic state, dynamic action) {
print('second ${state}');
switch (action) {
case SecondActions.Increment:
state++;
break;
case SecondActions.Decrement:
state--;
break;
case SecondActions.Reset:
state = 0;
break;
}
print('second ${state}');
return state;
}
// Define your State
class AppState {
final int firstState = 1;
final int secondState = 2;
AppState(firstState, secondState);
}
// Combine state
AppState appStateReducer(AppState state, action) => AppState(
firstReducer(state.firstState, action),
secondReducer(state.secondState, action)
);
// App store initialization, passing it to the app
void main() {
AppState combinedInitialState = AppState(3, 4);
final store = Store<AppState>(appStateReducer, initialState: combinedInitialState);
runApp(
MyApp(
store: store,
),
);
}
//Stateless because redux is handling the state, but we can mix them
class MyApp extends StatelessWidget {
MyApp({Key? key, required this.store}) : super(key: key);
final Store<dynamic> store;
@override
Widget build(BuildContext context) {
return StoreProvider<dynamic>(
store: store,
child: MaterialApp(
title: 'Flutter Redux',
// Start the app with the "/" named route. In this case, the app starts
// on the FirstScreen widget.
initialRoute: '/',
routes: {
// When navigating to the "/" route, build the FirstScreen widget.
'/': (context) => FirstScreen(store: store),
// When navigating to the "/second" route, build the SecondScreen widget.
'/second': (context) => SecondScreen(store: store),
},
),
);
}
}
// TODO: 4. show results
class FirstScreen extends StatelessWidget {
final Store<dynamic> store;
const FirstScreen({
required this.store,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return StoreProvider<dynamic>(
store: store,
child: Scaffold(
appBar: AppBar(
title: const Text('First Screen'),
),
body: Column(
children: [
Center(
child: ElevatedButton(
// Within the `FirstScreen` widget
onPressed: () {
// Navigate to the second screen using a named route.
Navigator.pushNamed(context, '/second');
},
child: const Text('Go to second screen'),
),
),
//Make a connector to get the updates when the store changes
StoreConnector<dynamic, String>(
converter: (store) => store.state.firstState.toString(),
builder: (context, count) {
return Text(
count,
style: TextStyle(fontSize: 24.0)
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(icon: Icon(Icons.add), onPressed: ()=> store.dispatch(FirstActions.Increment)),
IconButton(icon: Icon(Icons.remove), onPressed: ()=> store.dispatch(FirstActions.Decrement)),
],
),
StoreConnector<dynamic, String>(
converter: (store) => store.state.secondState.toString(),
builder: (context, count) {
return Text(
count,
style: TextStyle(fontSize: 24.0)
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(icon: Icon(Icons.add), onPressed: ()=> store.dispatch(SecondActions.Increment)),
IconButton(icon: Icon(Icons.remove), onPressed: ()=> store.dispatch(SecondActions.Decrement)),
],
),
],
),
),
);
}
}
class SecondScreen extends StatelessWidget {
final Store<dynamic> store;
const SecondScreen({
required this.store,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return StoreProvider<dynamic>(
store: store,
child: Scaffold(
appBar: AppBar(
title: const Text('Second Screen'),
),
body: Column(
children: [
ElevatedButton(
// Within the SecondScreen widget
onPressed: () {
// Navigate back to the first screen by popping the current route
// off the stack.
Navigator.pop(context);
},
child: const Text('Go back to first screen'),
),
//Make a connector to get the updates when the store changes
StoreConnector<dynamic, String>(
converter: (store) => store.state.toString(),
builder: (context, count) {
return Text(
count,
style: TextStyle(fontSize: 24.0)
);
},
),
ElevatedButton(
onPressed: () {
print('reset hit');
store.dispatch(FirstActions.Reset);
},
child: const Text('Reset'),
),
],
),
),
);
}
}
Solution 1:[1]
The problem was in the AppState class, the values should not have been final but should be constructed like this:
// Define your State
class AppState {
int firstState = 1;
int secondState = 2;
AppState(this.firstState, this.secondState);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | xyz83242 |