如何在Widget外部派发Bloc事件?

11

我对 Bloc 架构还不熟悉,最近看了一些教程。我有一个页面是 StatelessWidget 类型,并在其中使用 BlocProvider 初始化 UI 内的 Bloc,它将基于状态的更改返回不同的小部件。

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

如果我在另一个 Widget 中,我可以调用 BlocProvider.of<FirstTabBloc>(context).dispatch(GetUserProfile()); 但我无法在 main() 中调用它,因为 context 不是来自 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)中用到它? - nvoigt
我想在启动时调用API,其结果将更改状态/在屏幕上绘制不同的小部件。 - martin1337
在主函数中这样做可能不是一个好主意。你应该使用某种闪屏或用户交互界面。在主函数中进行重负载操作是不推荐的。 - nvoigt
2个回答

8
你需要使用另一种方法获取 bloc。 BlocProvider.of(context) 在内部使用 ProviderProvider 是一个flutter包,它包装了 InheritedWidgetInheritedWidget是flutter widget,通过context将数据传递到widget树下方。
所以你需要另一种方法。例如,你可以使用get_it库。它是dart实现的 服务定位器(Service Locator)
bloc在widget外部的简单示例
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 中注册你的 bloc。之后,你就可以在没有上下文的情况下获取和使用它。


我正在使用 'sl<FirstTabBloc>()'。sl 是 GetIt 实例。这意味着我可以在主函数中这样调用它:'sl<FirstTabBloc>().dispatch(GetUserProfile());' 吗? - martin1337
好的,我已经添加了示例。您可以以同样的方式执行,但不要在全局上下文中创建所需的块,而是使用get_it。 - Kherel
这是您要找的吗?还是您需要更好的示例? - Kherel

3

我认为你需要了解一下dispatch被重命名为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以获取更多细节信息。

希望对你有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接