brianegan / flutter_redux

A library that connects Widgets to a Redux Store
MIT License
1.65k stars 219 forks source link

[Question] is it expected that the store doesn't run until frames render? #246

Closed MilesAdamson closed 7 months ago

MilesAdamson commented 7 months ago

In my app, I have a process which loops. It is loading pages of data and takes a while. There are a lot of pages of data it's downloading in the background so it takes like 30 seconds to a minute to complete. It looks like this:

load action dispatched -> middleware loads page -> success action dispatched -> middleware checks success action to see if it's done -> if not dispatch load action

Now the weird thing is, if I start the app and don't touch anything, the process will stop until I touch the screen. If I put a widget on the screen which animates and continuously re-renders, it continues to loop until it's done.

Is there a way to make it so the store isn't tied to the rendering of the UI?

MilesAdamson commented 7 months ago

I could not re-create it with a smaller example project. This seems to count indefinitely

import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';

int counterReducer(int state, dynamic action) {
  // do nothing so we don't re-render
  return state;
}

void main() {
  final store = Store<int>(
    counterReducer,
    initialState: 0,
    middleware: [
      MyMiddleware(),
    ],
  );

  runApp(FlutterReduxApp(
    title: 'Flutter Redux Demo',
    store: store,
  ));
}

class FlutterReduxApp extends StatelessWidget {
  final Store<int> store;
  final String title;

  const FlutterReduxApp({super.key, required this.store, required this.title});

  @override
  Widget build(BuildContext context) {
    return StoreProvider<int>(
      store: store,
      child: MaterialApp(
        theme: ThemeData.dark(),
        title: title,
        home: Scaffold(
          appBar: AppBar(title: Text(title)),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                StoreConnector<int, String>(
                  distinct: true,
                  converter: (store) => store.state.toString(),
                  builder: (context, count) {
                    return Text(
                      'The button has been pushed this many times: $count',
                    );
                  },
                )
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () => store.dispatch(0),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        ),
      ),
    );
  }
}

class MyMiddleware implements MiddlewareClass<int> {
  @override
  call(Store<int> store, action, NextDispatcher next) async {
    if (action is int) {
      await Future.delayed(const Duration(milliseconds: 250));
      print(action);
      store.dispatch(action + 1);
    }

    next(action);
  }
}