'Flutter redux combined state dispatch action and update UI issue

I am learning redux. I have the following issues:

  1. My initial state does not seem to be taken into the account
  2. 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