首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何使用inheritedWidget中的流进行导航?

如何使用inheritedWidget中的流进行导航?
EN

Stack Overflow用户
提问于 2019-06-11 03:21:54
回答 1查看 751关注 0票数 1

我正在使用一个继承的Widget来访问一个包含一些长时间运行的任务(例如搜索)的Bloc。我想在第1页上触发搜索,并在完成后继续下一页。因此,我正在监听一个流,等待结果出现,然后导航到结果页面。现在,由于在initState()期间使用继承的小部件访问Bloc,我无法使用context.inheritFromWidgetOfExactType()访问bloc,并且在我阅读它时出现异常,因此建议在didChangeDependencies()中这样做。

这样做会导致一些奇怪的行为,因为我来回访问的频率越高,我访问的流就越频繁地触发,这将导致第二个页面被多次推送。而且随着每次来回交互,这一点会增加。我不明白为什么小溪会发生这种情况。欢迎您在这里发表任何见解。作为一种变通方法,我保留了一个保存状态的局部变量_onSecondPage,以避免多次推送到第二个页面。

现在我找到了How to call a method from InheritedWidget only once?,这对我的情况很有帮助,我可以通过context.ancestorInheritedElementForWidgetOfExactType()访问继承的小部件,只需监听流并直接从initState()导航到第二个页面。那么流的行为就像我所期望的那样,但问题是,这是否有任何其他的副作用,所以我应该通过在didChangeDependencides()中监听流来让它工作?

代码示例

我的FirstPage小部件监听流上的didChangeDependencies()。工作,但我想我错过了一些东西。我越频繁地从第一个页面导航到第二个页面,如果不保留本地_onSecondPage变量,第二个页面就会在导航堆栈上被推送多次。

代码语言:javascript
复制
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    debugPrint("counter: $_counter -Did change dependencies called");
    // This works the first time, after that going back and forth to the second screen is opened several times
    BlocProvider.of(context).bloc.finished.stream.listen((bool isFinished) {
       _handleRouting(isFinished);
    });
  }

  void _handleRouting(bool isFinished) async {
    if (isFinished && !_onSecondPage) {
      _onSecondPage = true;
      debugPrint("counter: $_counter -   finished: $isFinished : ${DateTime.now().toIso8601String()} => NAVIGATE TO OTHER PAGE");
      await Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => SecondRoute()),
      );
      _onSecondPage = false;
    } else {
      debugPrint("counter: $_counter -    finished: $isFinished : ${DateTime.now().toIso8601String()} => not finished, nothing to do now");
    }
  }

  @override
  void dispose() {
    debugPrint("counter: $_counter - disposing my homepage State");
    subscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            StreamBuilder(
              stream: BlocProvider.of(context).bloc.counter.stream,
              initialData: 0,
              builder: (context, snapshot) {
                _counter = snapshot.data;
                return Text(
                  "${snapshot.data}",
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

一个简单的Bloc伪装一些长期运行的工作

代码语言:javascript
复制
///Long Work Bloc
class LongWorkBloc {
  final BehaviorSubject<bool> startLongWork = BehaviorSubject<bool>();
  final BehaviorSubject<bool> finished = BehaviorSubject<bool>();

  int _counter = 0;
  final BehaviorSubject<int> counter = BehaviorSubject<int>();


  LongWorkBloc() {
    startLongWork.stream.listen((bool start) {
      if (start) {
        debugPrint("Start long running work");
        Future.delayed(Duration(seconds: 1), () => {}).then((Map<dynamic, dynamic> reslut) {
          _counter++;
          counter.sink.add(_counter);
          finished.sink.add(true);
          finished.sink.add(false);
        });
      }
    });
  }

  dispose() {
    startLongWork?.close();
    finished?.close();
    counter?.close();
  }
}

更好的工作代码

但是,如果我删除代码以访问从didChangeDependencies()继承的小部件并侦听initState()中的流,它似乎可以正常工作。

在这里,我获得了通过context.ancestorInheritedElementForWidgetOfExactType()保存流的继承小部件

这样做可以吗?或者,在这种情况下,flutter的最佳实践是什么?

代码语言:javascript
复制
  @override
  void initState() {
    super.initState();
    //this works, but I don't know if this is good practice or has any side effects?
    BlocProvider p = context.ancestorInheritedElementForWidgetOfExactType(BlocProvider)?.widget;
    if (p != null) {
      p.bloc.finished.stream.listen((bool isFinished) {
        _handleRouting(isFinished);
      });
    }
  }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-06-11 13:05:43

就我个人而言,我没有找到任何理由不在initState中侦听BLoC状态流。只要你记得在disposecancel你的订阅

如果您的BlocProvider正确使用了InheritedWidget,那么在initState中获取您的值应该不会有问题。

就像这样

代码语言:javascript
复制
  void initState() {
    super.initState();
    _counterBloc = BlocProvider.of(context);
    _subscription = _counterBloc.stateStream.listen((state) {
      if (state.total > 20) {
        Navigator.push(context,
            MaterialPageRoute(builder: (BuildContext context) {
          return TestPush();
        }));
      }
    });
  }

下面是一个在任何情况下都可以工作的优秀BlocProvider的示例

代码语言:javascript
复制
import 'package:flutter/widgets.dart';

import 'bloc_base.dart';

class BlocProvider<T extends BlocBase> extends StatefulWidget {
  final T bloc;
  final Widget child;

  BlocProvider({
    Key key,
    @required this.child,
    @required this.bloc,
  }) : super(key: key);

  @override
  _BlocProviderState<T> createState() => _BlocProviderState<T>();

  static T of<T extends BlocBase>(BuildContext context) {
    final type = _typeOf<_BlocProviderInherited<T>>();
    _BlocProviderInherited<T> provider =
        context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;
    return provider?.bloc;
  }

  static Type _typeOf<T>() => T;
}

class _BlocProviderState<T extends BlocBase> extends State<BlocProvider<BlocBase>> {
  @override
  Widget build(BuildContext context) {
    return _BlocProviderInherited<T>(
      bloc: widget.bloc,
      child: widget.child,
    );
  }

  @override
  void dispose() {
    widget.bloc?.dispose();
    super.dispose();
  }
}

class _BlocProviderInherited<T> extends InheritedWidget {
  final T bloc;

  _BlocProviderInherited({
    Key key,
    @required Widget child,
    @required this.bloc,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => false;
}

..。最后是BLoC

代码语言:javascript
复制
import 'dart:async';

import 'bloc_base.dart';

abstract class CounterEventBase {
  final int amount;
  CounterEventBase({this.amount = 1});
}

class CounterIncrementEvent extends CounterEventBase {
  CounterIncrementEvent({amount = 1}) : super(amount: amount);
}

class CounterDecrementEvent extends CounterEventBase {
  CounterDecrementEvent({amount = 1}) : super(amount: amount);
}

class CounterState {
  final int total;
  CounterState(this.total);
}

class CounterBloc extends BlocBase {
  CounterState _state = CounterState(0);

  // Input Streams/Sinks
  final _eventInController = StreamController<CounterEventBase>();
  Sink<CounterEventBase> get events => _eventInController;
  Stream<CounterEventBase> get _eventStream => _eventInController.stream;

  // Output Streams/Sinks
  final _stateOutController = StreamController<CounterState>.broadcast();
  Sink<CounterState> get _states => _stateOutController;
  Stream<CounterState> get stateStream => _stateOutController.stream;

  // Subscriptions
  final List<StreamSubscription> _subscriptions = [];

  CounterBloc() {
    _subscriptions.add(_eventStream.listen(_handleEvent));
  }

  _handleEvent(CounterEventBase event) async {
    if (event is CounterIncrementEvent) {
      _state = (CounterState(_state.total + event.amount));
    } else if (event is CounterDecrementEvent) {
      _state = (CounterState(_state.total - event.amount));
    }
    _states.add(_state);
  }

  @override
  void dispose() {
    _eventInController.close();
    _stateOutController.close();
    _subscriptions.forEach((StreamSubscription sub) => sub.cancel());
  }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56532375

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档