【问题标题】:Using multiple BlocConsumer for the same bloc on the same page对同一页面上的同一块使用多个 BlocConsumer
【发布时间】:2021-08-13 03:50:45
【问题描述】:

我在“MyButton”小部件中使用BlocConsumer,该小部件用于列表中的所有项目。 当用户按下按钮时,我想根据用户单击的按钮将用户重定向到另一个页面(在下面的代码中,将打印按钮文本)。

如果我运行此代码,单击按钮会打印所有文本(“Press 1”、“Press 2”、“Press 3”)。 是因为完全相同的侦听器附加到 MyButton 小部件 3 次,并且每当单击任何按钮时都会调用所有侦听器? 这是否意味着同一页面上的一个块不应有多个小部件?

我可以通过移动 BlocConsumer 来包装 Column 而不是 MyButton 来修复代码。这是处理这个问题的正确方法吗?如果是这种情况,我必须在更复杂的情况下包装一个更大的小部件。例如,如果 appbar 和 body 中的小部件使用相同的 bloc,那么我将不得不用 BlocConsumer 包装整个页面。

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

void main() async {
  runApp(BlocProvider<MyBloc>(
    create: (BuildContext context) => MyBloc(),
    child: MaterialApp(
      home: Column(
        children: [
          MyButton(text: "Press 1"),
          MyButton(text: "Press 2"),
          MyButton(text: "Press 3"),
        ],
      ),
    ),
  ));
}

class MyButton extends StatelessWidget {
  const MyButton({Key? key, required this.text}) : super(key: key);

  final String text;

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<MyBloc, MyState>(
      listenWhen: (previous, current) {
        return true;
      },
      listener: (context, state) {
        print(text);
      },
      builder: (context, state) {
        return TextButton(
          key: UniqueKey(),
          child: Text(text),
          onPressed: () {
            context.read<MyBloc>().add(MyEvent());
          },
        );
      },
    );
  }
}

class MyEvent {}

class MyState {}

class MyBloc extends Bloc<MyEvent, MyState> {
  MyBloc() : super(MyState());

  @override
  Stream<MyState> mapEventToState(MyEvent event) async* {
    yield MyState();
  }
}

【问题讨论】:

    标签: flutter bloc


    【解决方案1】:

    要处理bloc 中的不同点击,您必须定义事件和状态。

    // events
    class FirstClick extends Event{}
    
    class SecondClick extends Event{}
    
    class ThirdClick extends Event{}
    
    // states
    class FirstClickState extends State{}
    
    class SecondClickState extends State{}
    
    class ThirdClickState extends State{}
    
    // bloc function 
    class MyBloc extends Bloc<MyEvent, MyState> {
    MyBloc() : super(MyState());
    
      @override
      Stream<MyState> mapEventToState(MyEvent event) async* {
        if(event is FirstClick){
          yield FirstClickState();
        }
    
        if(event is SecondClick){
          yield SecondClickState();
        }
    
        if(event is ThirdClick){
          yield ThirdClickState();
        }
      }  
    }
    
    // view definition
    import 'package:flutter/material.dart';
    import 'package:flutter_bloc/flutter_bloc.dart';
    
    void main() async {
      runApp(BlocProvider<MyBloc>(
        create: (BuildContext context) => MyBloc(),
        child: MaterialApp(
        home: Column(
          children: [
            MyButton(text: "Press 1", onPressed:() => BlocProvider.of<MyBloc>(context).add(FirstClick())),
            MyButton(text: "Press 2", onPressed:() => BlocProvider.of<MyBloc>(context).add(SecondClick())),
            MyButton(text: "Press 3", onPressed:() => BlocProvider.of<MyBloc>(context).add(ThirdClick())),
            ],
          ),
        ),
      ));
    }
    
    class MyButton extends StatelessWidget {
    const MyButton({Key? key, required this.text}) : super(key: key);
    
    final String text;
    
    @override
    Widget build(BuildContext context) {
      return BlocConsumer<MyBloc, MyState>(
        listenWhen: (previous, current) {
          return true;
        },
        listener: (context, state) {
          if(state is FirstClickState){
            // go to where ever 
            Navigator.push(context, Somewhere());
          }
          
          if(state is SecondClickState){
            // go to where ever 
            Navigator.push(context, Somewhere());
          }
    
          if(state is ThirdClickState){
            // go to where ever 
            Navigator.push(context, Somewhere());
          }
        },
        builder: (context, state) {
          return TextButton(
            key: UniqueKey(),
            child: Text(text),
            onPressed: () {
              context.read<MyBloc>().add(MyEvent());
            },
          );
        },
      );
     }
    }
    

    【讨论】:

    • 我的示例只有 3 个按钮,但它可以有更多。更糟糕的是,按钮可能不是同一类型(例如,AppBar 中的按钮和正文中的按钮)。
    【解决方案2】:

    如果您有未定义的页数,我会使用状态类型为 int 的 Cubit。这可以让你拥有尽可能多的页面,并且页面的索引可以用作肘部的状态。

    class PagesCubit extends Cubit<int> {
      PagesCubit() : super(0);
    
      void selectPage(int index) => emit(index);
    
    }
    

    在你的代码中,你可以这样使用它。

    BlocProvider<PagesCubit>(
      lazy: false,
      create: (BuildContext context) => PagesCubit(),
      child: BlocListener<PagesCubit, int>(
        listener: (context, state) {
          print(state); // expects int value
          //TODO: navigate to page
        },
        child: MaterialApp(
        home: Column(
            children: [
              MyButton(text: "Press 1"),
              MyButton(text: "Press 2"),
              MyButton(text: "Press 3"),
            ],
          ),
        ),
      ),
    )
    
    //somewhere anywhere else in your code
    context.read<PagesCubit>().selectPage(index);
    

    注意:不要在按钮内部处理导航,除非您知道要导航到的页面。您最终会复制和粘贴大量代码。相反,将导航拉上一层(例如,包裹在您的 Column 周围)并在那里管理导航。您可以这样做,因为 BLoC 允许您将代码移动到它为您的块提供的树中的任何位置。

    【讨论】:

      猜你喜欢
      • 2021-09-04
      • 2021-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-06
      • 2011-08-03
      相关资源
      最近更新 更多