Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库

Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库

alt_bloc 概述

alt_bloc 是一个用于实现 BLoC 设计模式的库。它由 DashDevs LLC 和 Ingenio LLC 开发。

作者

  • Yegor Logachev <yegor.logachev@dashdevs.com> (DashDevs LLC)
  • Olivier Brand <obrand@ingenio.com> (Ingenio LLC)

引言

为什么我们决定创建这个解决方案?

我们非常兴奋地推出我们版本的 BLoC 模式。从 Flutter 发布之初,我们就一直在实现 Flutter 应用,并积累了丰富的框架经验。我们的主要目标是提供一种简单易懂且快速采用的状态管理解决方案,以构建强大的应用程序。我们还希望避免依赖复杂的“魔法”框架(如 Rx),或者不得不使用多个框架来达到相同的效果。

现有的解决方案,如 blocflutter_bloc,在设计上存在一些限制:

  • bloc 库只能处理单一状态和事件类。因此,开发者必须创建大型的继承层次结构,这导致状态和事件类的数量随着块的数量呈非线性增长。
  • bloc 库使得在其他 Bloc 类中创建可重用状态变得困难。例如,简单的 UX 元素如进度指示器会导致每个 Bloc 都包含一个状态类。
  • blocflutter_bloc 库依赖于复杂的库和包,如 rxdart 和 provider,这会影响构建时间和项目大小,并增加调试复杂性。

主要特性

  • alt_bloc 的公共接口与流行的库(如 ProviderBLoC)类似,所以你无需花费大量时间学习如何设置该库。
  • 支持多状态。你可以在每个 Bloc 中创建几个状态,并且不需要创建继承自单一父状态的状态层次结构。
  • 支持 StreamFuture 对象作为状态提供者。
  • 提供独立的管道来处理导航操作。
  • 包含接收导航结果的方法。
  • 轻量级解决方案。整个包非常小,不包含任何第三方库。

组件

Bloc

业务逻辑组件。

该类实现了核心业务逻辑,并应放置在 UI 和数据源之间。BLoC 接受来自 widget 的 UI 动作,并通知 widget 关于 UI 状态的变化或发起导航操作。

该类实现了一些方法,以简化向 widget 交付业务逻辑状态变化和导航操作的过程。

当前解决方案包含注册要提供给 widget 的状态的方法 (registerState())、发送这些状态的方法 (addState(), addStateSource(), addStatesSource()) 和导航操作的方法 (addNavigation(), addNavigationSource()).

class CounterBloc extends Bloc {
  int value = 0;

  CounterBloc() {
    registerState<int>(initialState: value);
  }

  void increment() => addState(++value);
}

widget 应使用 getStateStream()navigationStream 属性来订阅 Bloc 事件。

abstract class Bloc {
  void registerState<S>({bool isBroadcast = false, S initialState});
  S initialState<S>();
  bool containsState<S>();
  bool get isClosed;
  bool addState<S>(S uiState);
  StreamSubscription<S> addStateSource<S>(Future<S> source, {void Function(S data) onData, void Function() onDone, void Function(dynamic error) onError});
  StreamSubscription<S> addStatesSource<S>(Stream<S> source, {void Function(S data) onData, void Function() onDone, void Function(dynamic error) onError});
  Future<Result> addNavigation<Result>({String routeName, dynamic arguments});
  StreamSubscription<RouteData> addNavigationSource(Stream<RouteData> source, {void Function(RouteData data) onData, void Function() onDone, void Function(dynamic error) onError});
  Stream<S> getStateStream<S>();
  Stream<RouteData> get navigationStream;
  void close();
}

RouteData

包含所有有关导航的信息的类。

ResultConsumer

用于将导航结果返回到 Bloc 的回调签名。

BlocProvider

负责通过 create 函数创建 BlocWidget。接受任何 Widget 作为子节点,并允许子节点获取 Bloc

函数 shouldNotify 定义了继承自此 widget 的 widget 是否应在 Bloc 更改时重建。

class CounterScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return BlocProvider<CounterBloc>(
      routerPrecondition: (prevRouteData, routeData) => (routeData.settings.arguments as int) % 5 == 0,
      create: () => CounterBloc(),
      child: CounterLayout(title: 'Bloc Demo Home Page'),
      router: (context, name, args) {
        return showDialog(
          context: context,
          builder: (_) {
            return WillPopScope(
              child: AlertDialog(
                title: Text('Congratulations! You clicked $args times'),
              ),
              onWillPop: () async {
                Navigator.of(context).pop('Dialog with $args clicks has been closed');
                return false;
              }
            );
          }
        );
      },
    );
  }
}

Provider

封装在 BlocProvider 中的 InheritedWidget,允许 BlocProvider.child widget 树获取 Bloc 对象。

static B of<B extends Bloc>(BuildContext context, {bool listen = false})

BlocBuilder

接受类型为 BBloc 并订阅类型为 S 的状态流的 Widget。如果未提供 Bloc,则默认使用 Provider.of

BlocBuilder<CounterBloc, int>(
  bloc: CounterBloc(),
  precondition: (prevCount, count) => count % 2 == 0,
  builder: (_, count) => Text('$count');
)

BlocConsumer

接受类型为 BBloc 并订阅类型为 S 的状态流的 Widget。如果未提供 Bloc,则默认使用 Provider.of

RouteListener

订阅 Bloc.navigationStream,监听并处理导航操作的 Widget。如果未提供 Bloc,则默认使用 Provider.of

RouteListener<CounterBloc>(
  bloc: CounterBloc(),
  child: CounterLayout(title: 'Bloc Demo Home Page'),
  precondition: (prevData, data) => (data.settings.arguments as int) % 5 == 0,
  router: (context, name, args) {
    return showDialog(
      context: context,
      builder: (_) {
        return WillPopScope(
          child: AlertDialog(
            title: Text('Congratulations! You clicked $args times'),
          ),
          onWillPop: () async {
            // Argument that Navigator.pop() function will be returned to the Bloc     
            Navigator.of(context).pop('Dialog with $args clicks has been closed');
            return false;
          }
        );
      }
    );
  },
)

示例

以下是一个简单的 Bloc 实现示例。

你需要使用 registerState<T>() 方法注册 Bloc 将使用的所有状态。为了通知 Widget 关于更改,你应该调用 addState<T>(T_object);

class CounterBloc extends Bloc {
  var _counter = 0;

  CounterBloc() {
    registerState<int>(initialState: 10);
    registerState<bool>(initialState: false);
  }

  void increment() {
    addState<bool>(true);
    Future.delayed(const Duration(milliseconds: 500), () {
      addState<bool>(false);
      addState<int>(++_counter);
      if ((_counter % 10) == 0) {
        addNavigation(arguments: _counter);
      }
    });
  }
}

如果你有一些包含状态对象的 FutureStream,你可以使用 addStatesSource()addStateSource() 函数将其作为状态源传递。

class IncrementRepo {
  int _counter = 0;

  Future<int> increment() => Future.delayed(const Duration(seconds: 1), () => ++_counter);

  Future<int> decrement() => Future.delayed(const Duration(seconds: 5), () => --_counter); 
}

class CounterBloc extends Bloc {
  final repo = IncrementRepo();

  CounterBloc() {
    registerState<int>(initialState: 0);
    registerState<bool>(initialState: false);
  }

  void increment() {
    addState<bool>(true);
    addStateSource<int>(repo.increment(),
        onData: (count) async {
          print('Dialog result: ${await addNavigation(arguments: count)}');
        },
        onDone: () => addState<bool>(false));
  }
}

BlocProvider

create 函数负责 Bloc 的创建。

class CounterScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return BlocProvider<CounterBloc>(
      create: () => CounterBloc(),
      routerPrecondition: (_, routeData) => (routeData.settings.arguments as int) % 5 == 0,
      child: CounterLayout(title: 'Bloc Demo Home Page'),
      router: (context, name, args) {
        return showDialog(
          context: context,
          builder: (_) => WillPopScope(
            child: AlertDialog(
              title: Text('Congratulations! You clicked $args times'),
            ),
            onWillPop: () async {
              Navigator.of(context).pop(args);
              return false;
            }
          )
        );
      },
    );
  }
}

导航处理

你可以使用 router 如上所示来处理导航。或者你可以使用 RouteListener

class CounterScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return BlocProvider<CounterBloc>(
      create: () => CounterBloc(),
      child: RouteListener<CounterBloc>(
        child: CounterLayout(title: 'Bloc Demo Home Page'),
        router: (context, name, args) {
          return showDialog(
            context: context,
            builder: (_) => AlertDialog(
              title: Text('Congratulations! You clicked $args times'),
            ),
          );
        },
      ),
    );
  }
}

在 UI 上提供和观察 Bloc

class CounterLayout extends StatelessWidget {
  CounterLayout({Key key, this.title}) : super(key: key);

  final String title;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Stack(
        children: <Widget>[
          Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                BlocBuilder<CounterBloc, int>(
                    builder: (_, count) {
                      return Text(
                        '$count',
                        style: Theme.of(context).textTheme.display1,
                      );
                    }),
              ],
            ),
          ),
          Center(
            child: BlocBuilder<CounterBloc, bool>(
              builder: (_, inProgress) {
                return inProgress ? CircularProgressIndicator() : Container();
              }
            ),
          )
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: Provider.of<CounterBloc>(context).increment,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

更多关于Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,alt_bloc作为一个“未知功能”的插件(假设其功能未明确定义),我们可以从基本的插件集成和使用方法来探讨其可能的实现方式。由于具体功能未知,这里将提供一个假设性的集成代码示例,展示如何在Flutter项目中引入并使用一个假设的第三方插件。

首先,假设alt_bloc已经发布在pub.dev上,我们可以通过pubspec.yaml文件添加依赖:

dependencies:
  flutter:
    sdk: flutter
  alt_bloc: ^x.y.z  # 假设的版本号

然后运行flutter pub get来安装依赖。

接下来,我们将展示如何在Flutter应用中使用这个假设的alt_bloc插件。由于具体功能未知,我们将创建一个假设性的使用场景,比如初始化插件、监听事件或状态变化等。

import 'package:flutter/material.dart';
import 'package:alt_bloc/alt_bloc.dart';  // 假设的导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Alt Bloc Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AltBlocExampleScreen(),
    );
  }
}

class AltBlocExampleScreen extends StatefulWidget {
  @override
  _AltBlocExampleScreenState createState() => _AltBlocExampleScreenState();
}

class _AltBlocExampleScreenState extends State<AltBlocExampleScreen> {
  // 假设AltBloc是插件提供的一个Bloc类
  AltBloc? _altBloc;

  @override
  void initState() {
    super.initState();
    // 初始化AltBloc实例
    _altBloc = AltBloc();

    // 假设监听某个事件或状态变化
    _altBloc!.someStream.listen((event) {
      // 处理事件或状态变化
      print('Received event: $event');
    });
  }

  @override
  void dispose() {
    // 释放资源
    _altBloc?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Alt Bloc Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 假设触发某个动作
            _altBloc?.someAction();
          },
          child: Text('Trigger Action'),
        ),
      ),
    );
  }
}

// 假设AltBloc类的定义(这部分通常会在alt_bloc插件中提供)
class AltBloc {
  // 假设的Stream用于监听事件或状态变化
  final StreamController<String> _controller = StreamController<String>();
  Stream<String> get someStream => _controller.stream;

  // 假设的动作方法
  void someAction() {
    // 触发某个事件或状态变化
    _controller.add('Action Triggered');
  }

  // 释放资源
  void dispose() {
    _controller.close();
  }
}

注意:上述代码中的AltBloc类及其方法(如someStreamsomeAction)完全是假设性的,用于展示如何在Flutter中集成和使用一个第三方插件。在实际使用中,你需要根据alt_bloc插件的文档来了解其提供的具体API和功能。

由于alt_bloc的具体功能未知,上述代码仅作为一个集成和使用第三方插件的模板示例。在实际项目中,你应该查阅alt_bloc的官方文档或源代码来了解其真实的功能和使用方法。

回到顶部