如何在 Widget 之外调度 Bloc 事件?

Posted

技术标签:

【中文标题】如何在 Widget 之外调度 Bloc 事件?【英文标题】:How to dispatch Bloc events outside of Widget? 【发布时间】:2020-12-07 09:10:15 【问题描述】:

我是 Bloc 架构的新手,最近看了一些教程。我有一个 StatelessWidget 页面,在里面我使用 BlocProvider 在我的 UI 中初始化 Bloc。它将根据状态变化返回不同的小部件。

但即使在教程中,它们也仅从其他小部件(例如通过按下按钮)分派事件。但在我的情况下,我需要在 Bloc Event 中的 Widget 之外调用 init API。

如果我在其他 Widget 中,我可以调用 BlocProvider.of<FirstTabBloc>(context).dispatch(GetUserProfile());,但我不能在 main() 中调用它,因为上下文不是来自 Widget。

代码:

class FirstPage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: SingleChildScrollView(
        child: firstTabElement(context),
      ),
      appBar: EmptyAppBar(mainAppColor),
    );
  

  BlocProvider<FirstTabBloc> firstTabElement(BuildContext context) 
    return BlocProvider(
        builder: (_) => sl<FirstTabBloc>(),
        child: Expanded(
            child: Container(
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          child: Column(
            children: <Widget>[
              Stack(
                children: [
                  //Main Background
                  Column(children: <Widget>[
                    //Top part
                    Container(
                      constraints: BoxConstraints(
                        minHeight: 120,
                      ),
                      width: MediaQuery.of(context).size.width,
                      height: 300,
                      decoration: new BoxDecoration(
                        gradient: LinearGradient(
                          begin: Alignment.topCenter,
                          end: Alignment.bottomCenter,
                          colors: ticketGradient,
                        ),
                      ),
                    ),
                    // Bottom part - white
                    Container(
                      color: basicWhite,
                    ),
                  ]),
                  //content above background
                  BlocBuilder<FirstTabBloc, FirstTabState>(
                    builder: (context, state) 
                      if (state is Uninitialized) 
                        return WalletWidget(
                          credit: formatPrice(0),
                          currency: '€',
                        );
                       else if (state is Loading) 
                        return LoadingWidget();
                       else if (state is Loaded) 
                        Wallet currWallet = state.profile.getWalletByClient(clientId);
                        return WalletWidget(
                          credit: formatPrice(currWallet.value),
                          currency: formatCurrency(currWallet.currency),
                        );
                       else if (state is Error) 
                        return MessageDisplay(
                          message: state.message,
                        );
                      
                    ,
                  ),
                ],
              ),
            ],
          ),
        )));
  

【问题讨论】:

为什么在 main 中需要它? 我想在启动时调用 API,结果会改变状态/在屏幕上绘制不同的小部件。 在 main 中这样做可能不是一个好主意。您应该使用某种闪屏或用户交互的东西。不建议在 main 中进行繁重的工作。 【参考方案1】:

你需要用另一种方式来获得集团。

BlocProvider.of(context) 在后台使用Provider。 Provider 是一个颤振包,它包裹了 InheritedWidget。 InheritedWidget 是 Fl​​utter 小部件,它通过上下文将数据向下传递到小部件树。

所以你需要另一种方式。例如,您可以使用 get_it 库。这是Service Locator 的飞镖实现。

小部件外部块的简单示例

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

final bloc = CounterBloc();

void main() 
  bloc.add(CounterEvent.increment);
  bloc.add(CounterEvent.increment);
  bloc.add(CounterEvent.increment);
  bloc.add(CounterEvent.increment);

  runApp(
    MyApp(),
  );


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: SafeArea(
          child: MyHomePage(),
        ),
      ),
    );
  


class MyHomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return BlocProvider(
      create: (_) => bloc,
      child: CounterPage(),
    );
  


class CounterPage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: BlocBuilder<CounterBloc, int>(
        builder: (_, count) 
          return Center(
            child: Text('$count', style: Theme.of(context).textTheme.headline1),
          );
        ,
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: const Icon(Icons.add),
              onPressed: () =>
                  context.bloc<CounterBloc>().add(CounterEvent.increment),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: const Icon(Icons.remove),
              onPressed: () =>
                  context.bloc<CounterBloc>().add(CounterEvent.decrement),
            ),
          ),
        ],
      ),
    );
  


enum CounterEvent  increment, decrement 

class CounterBloc extends Bloc<CounterEvent, int> 
  CounterBloc() : super(0);

  @override
  Stream<int> mapEventToState(CounterEvent event) async* 
    switch (event) 
      case CounterEvent.decrement:
        yield state - 1;
        break;
      case CounterEvent.increment:
        yield state + 1;
        break;
      default:
        addError(Exception('unsupported event'));
    
  

在 get_it 中注册您的集团。之后,您可以在没有上下文的情况下获取并使用它。

【讨论】:

我正在使用它'sl()'。 sl 是 GetIt 实例。这意味着我可以在 main 中调用它,例如 'sl().dispatch(GetUserProfile());' ? 好。我已经添加了示例。您可以以相同的方式执行此操作,但不是在全局上下文中而是使用 get_it 创建所需的块。 这是你要找的还是你需要更好的例子?【参考方案2】:

我想你需要知道 dispatch renamed to add

> There were core api changes introduced into 1.0.0:
bloc.state.listen -> bloc.listen
bloc.currentState -> bloc.state
bloc.dispatch -> bloc.add
bloc.dispose -> bloc.close

查看https://link.medium.com/qnfMcEcW00 了解更多详情。 希望对你有帮助?

【讨论】:

以上是关于如何在 Widget 之外调度 Bloc 事件?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Bloc 如何从 Widget 本身更新 BlocBuilder 中的 Widget?

调度第一个 bloc 事件或 cubit 方法,在 StatelessWidget 内的页面开始

在使用 flutter_bloc 库调度事件之前等待一些结果

[UE4]Child Widget中的事件调度器

Dart/Flutter 中 BLoC 模式的最佳实践

Flutter BLoC:在父 Widget 更新时维护子状态