google / flutter_flux

Implementation of the Flux framework for Flutter
https://flutter.io
Apache License 2.0
372 stars 36 forks source link

Method listening to an action gets called multiple times #44

Open DhudumVishal opened 5 years ago

DhudumVishal commented 5 years ago

If a method is listening to an action then for first time it gets hit once, then for next time if user gets back to the same page then the method gets hit twice and on third time its 3 times and increases every time.

Below is a code to simulate the same on click of Next a action is called and on action change the user is taken to second page, but on second time the action listening method gets hit twice.

    import 'package:flutter/material.dart';
    import 'package:flutter_flux/flutter_flux.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MyHome(),
        );
      }
    }
    //========
    class MyHome extends StatefulWidget {
      @override
      _MyHomeState createState() => _MyHomeState();
    }

    class _MyHomeState extends State<MyHome> with StoreWatcherMixin<MyHome>  {
      AppStore store;
      @override
      void initState() {
        store = listenToStore(appStoreToken);
        changeStatus.listen(_openSetupDialogue);
        super.initState();
      }

      void _openSetupDialogue(dynamic payload){
        if(payload == true) {
          print("method got hit");
          Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));
        }
      }

      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          child: Text("Next"),
          onPressed: (){
            changeStatus(true);
          },
        );
      }
    }
    //===========
    class SecondPage extends StatefulWidget {
      @override
      _SecondPageState createState() => _SecondPageState();
    }

    class _SecondPageState extends State<SecondPage> {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          child: Text("back"),
          onPressed: (){Navigator.push(context, MaterialPageRoute(builder: (context) =>MyHome()),
            );
          },
        );
      }
    }
    //===========
    class AppStore extends Store {
      bool state = true;
      AppStore(){
        triggerOnAction(changeStatus, (status){
          state = status;
        });
      }
      bool get getAppStore => state;
    }
    final Action changeStatus = Action<bool>();
    final appStoreToken = StoreToken(AppStore());
iptton commented 5 years ago

maybe you should call unlistenFromStore in dispose method ?

  @override
  void dispose() {
    super.dispose();
    unlistenFromStore(store);
  }
DhudumVishal commented 5 years ago

@iptton I did tried your suggestion but it the issue is still there, the method gets hit multiple times. Bellow is the updated code import 'package:flutter/material.dart'; import 'package:flutter_flux/flutter_flux.dart';

  void main() => runApp(MyApp());

  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        home: FirstPage(),
      );
    }
  }

  //===========
  class FirstPage extends StatefulWidget {
    @override
    _FirstPageState createState() => _FirstPageState();
  }

  class _FirstPageState extends State<FirstPage> {
    @override
    Widget build(BuildContext context) {
      return RaisedButton(
        child: Text("next"),
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));
        },
      );
    }
  }

  //========
  class SecondPage extends StatefulWidget {
    @override
    _SecondPageState createState() => _SecondPageState();
  }

  class _SecondPageState extends State<SecondPage> with StoreWatcherMixin<SecondPage>  {
    AppStore store;
    @override
    void initState() {
      store = listenToStore(appStoreToken);
      store.listen(_openSetupDialogue);
      super.initState();
    }
    @override
    void dispose() {
      super.dispose();
      unlistenFromStore(store);
    }

    void _openSetupDialogue(dynamic payload){
        print("method got hit");
    }

    @override
    Widget build(BuildContext context) {
      return RaisedButton(
        child: Text("back"),
        onPressed: (){
          changeStatus(true);
          Navigator.pop(context);
        },
      );
    }
  }

  //===========
  class AppStore extends Store {
    bool state = true;
    AppStore(){
      triggerOnAction(changeStatus, (status){
        state = status;
      });
    }
    bool get getAppStore => state;
  }
  final Action changeStatus = Action<bool>();
  final appStoreToken = StoreToken(AppStore());
DhudumVishal commented 5 years ago

The issue is unlistenFromStore(store); is not working in my case,

Workaround: add listener only once on App start up.

mvresh commented 4 years ago

Instead, you could try calling unlistenFromStore(store); when the routing takes place. Reason why it's not working in your case is that it's not being disposed in the first place.