如何在BLoC中使用BottomNavigationBar?

时间:2018-12-25 16:15:23

标签: dart flutter

当我将BottomNavigationBar与BLoC模式一起使用时,会导致错误Bad state: Stream has already been listened to.

我可能只在一个地方收听BLoC流。

我的代码如下。

main.dart

import 'package:flutter/material.dart';
import 'package:bottom_tab_bloc/app_state_bloc.dart';
import 'package:bloc_provider/bloc_provider.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      creator: (context, _bag) => AppStateBloc(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String activeTab = "tab1";
  final bottomTabs = ["tab1", "tab2"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(activeTab),
      ),
      body: buildTab(activeTab),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: bottomTabs.indexOf(activeTab),
        onTap: (int index) {
          setState(() {
            activeTab = bottomTabs[index];
          });
        },
        items: bottomTabs.map((tab) =>
          buildBnbItem(tab)
        ).toList(),
      ),
    );
  }

  Widget buildTab(String tab) {
    if (tab == "tab1") {
      return TabOne();
    } else if (tab == "tab2") {
      return TabTwo();
    }
  }

  BottomNavigationBarItem buildBnbItem (String tab) {
    assert(bottomTabs.contains(tab));

    if (tab == "tab1") {
      return BottomNavigationBarItem(
        title: Text('Tab1'),
        icon: Icon(Icons.looks_one),
      );
    } else if (tab == "tab2") {
      return BottomNavigationBarItem(
        title: Text('Tab1'),
        icon: Icon(Icons.looks_two),
      );
    }
  }
}

class TabOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final AppStateBloc bloc = BlocProvider.of<AppStateBloc>(context);
    return StreamBuilder(
      stream: bloc.outValue1,
      builder: (context, snapshot) =>
        Center(child: Text(snapshot.data.toString())),
    );
  }
}

class TabTwo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final AppStateBloc bloc = BlocProvider.of<AppStateBloc>(context);
    return StreamBuilder(
      stream: bloc.outValue2,
      builder: (context, snapshot) =>
        Center(child: Text(snapshot.data.toString())),
    );
  }
}

app_state_bloc.dart

import 'dart:async';
import 'package:bloc_provider/bloc_provider.dart';

class AppStateBloc implements Bloc {
  StreamController<int> _value1Controller
    = StreamController<int>();
  Sink<int> get _inValue1 => _value1Controller.sink;
  Stream<int> get outValue1 => _value1Controller.stream;

  StreamController<int> _updateValue1Controller
    = StreamController<int>();
  Sink<int> get updateValue1 =>
      _updateValue1Controller.sink;

  StreamController<int> _value2Controller
  = StreamController<int>();
  Sink<int> get _inValue2 => _value2Controller.sink;
  Stream<int> get outValue2 => _value2Controller.stream;

  StreamController<int> _updateValue2Controller
  = StreamController<int>();
  Sink<int> get updateValue2 =>
      _updateValue2Controller.sink;

  AppStateBloc(){
    _inValue1.add(1);
    _updateValue1Controller.stream.listen(_updateValue1);
    _inValue2.add(2);
    _updateValue2Controller.stream.listen(_updateValue2);
  }

  @override
  void dispose() {
    _value1Controller.close();
    _updateValue1Controller.close();
    _value2Controller.close();
    _updateValue2Controller.close();
  }

  void _updateValue1(int value1) {
    _inValue1.add(value1);
  }

  void _updateValue2(int value2) {
    _inValue2.add(value2);
  }
}

我只能第一次从TabTwo转到TabOne,但是当我回到TabOne时会发生错误。

我也尝试在StreamController<int>.broadcast()中使用app_state_bloc.dart,但是snapshot.data始终是null

  • 如何使用BLoC模式实施BottomNavigationBar
  • 为什么我只在一个地方写每个流,但流被调用了两次以上?
  • 此代码中是否调用AppStateBloc.dispose()?何时何地调用AppStateBloc.dispose()
  • 为什么广播流的snapshot.data始终为空?

1 个答案:

答案 0 :(得分:1)

  

为什么流被调用两次以上,尽管我写了每个流   在一个地方?

该错误与流的订阅者数量有关。默认情况下,StreamController仅允许一个订阅者。这就是TabOne第一次可以正常运行,但随后会中断的原因。

  

此代码中是否调用了AppStateBloc.dispose()?何时何地   是AppStateBloc.dispose()吗?

在删除BlocProvider小部件时会调用它,但是由于它被用作应用程序的根目录,所以我猜这只会在应用程序关闭时发生。

  

为什么广播流的snapshot.data始终为null?

没有侦听器时,广播流不会缓冲事件。由于您是在创建TabOne之前写流,因此该事件丢失了,您得到了null

  

如何使用BLoC模式实现BottomNavigationBar?

我想这取决于您的用例,但是对于此特定示例,如果将rxdartBehaviorSubject替换为StreamController,则可以正常工作,因为这样您就可以总是向您发送最后一个事件的广播流。