Flutter状态管理架构插件bloc_toolkit的使用

发布于 1周前 作者 itying888 来自 Flutter

Flutter状态管理架构插件 bloc_toolkit 的使用

bloc_toolkit 是一个用于在Flutter应用中高效灵活地进行状态管理的插件,基于Bloc模式。它旨在简化应用程序开发,提供高级功能如加载、重新加载、更新和初始化数据。

DataBloc

DataBloc 类是该包的核心。这是一个通用类,提供了管理数据源状态的一整套工具,并设计为实现业务逻辑的基础类。

创建你的DataBloc

class AnimalBloc extends DataBloc<String, String> {
  AnimalBloc({required AnimalRepository animalRepository})
      : _animalRepository = animalRepository;
  final AnimalRepository _animalRepository;

  [@override](/user/override)
  FutureOr<String> loadData(DataS<String> oldState, LoadDataE<String> event) {
    return _animalRepository.getAnimal(event.params!);
  }
}

构建你的Widgets

BlocProvider(
  create: (_) => AnimalBloc(animalRepository: AnimalRepository()),
  child: BlocConsumer<AnimalBloc, DataS<String>>(
    listener: (context, state) {
      if (state is ErrorS<String>) {
        _showSnackBar(context, 'Loading animal error: ${state.error}');
      } 
    },
    builder: (context, state) {
      if (state is UnloadedDataS<String>) {
        return Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text('Animal not loaded'),
            ElevatedButton(
              onPressed: () {
                context.read<AnimalBloc>().add(const LoadDataE(params: 'some args'));
              },
              child: const Text('Load Animal'),
            ),
          ],
        );
      }
      if (state is LoadingDataS<String>) {
        return const Text('Loading animal...');
      }
      if (state is LoadedDataS<String, String>) {
        return Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(state.data),
            ElevatedButton(
              onPressed: () {
                context.read<AnimalBloc>().add(const ReloadDataE(params: 'some args'));
              },
              child: const Text('Reload Animal'),
            ),
          ],
        );
      }
      if (state is ReloadingDataS<String, String>) {
        return const Text('Reloading animal...');
      }
      return const SizedBox();
    },
  ),
)

添加事件

final animalBloc = context.read<AnimalBloc>();

// 加载数据
animalBloc.add(const LoadDataE(params: 'some args'));       
// 或者 初始化数据
animalBloc.add(InitializeDataE('dog')) 

// 然后你可以
animalBloc.add(const ReloadDataE(params: 'some args')) 
// 或者 更新数据
animalBloc.add(UpdateDataE((currentData) => 'cat'));

状态

DataBloc 支持以下几种状态:

  • 基础状态(Base States)

    • abstract DataS: 所有状态的基础。
    • abstract IdleS: 当没有事情发生时的状态。
    • abstract LoadingS: 数据正在加载或重新加载时的状态。
    • abstract ErrorS: 发生错误时的状态。
  • 数据未加载状态(Data unloaded states)

    • UnloadedDataS: 初始状态,没有任何数据被加载。
    • LoadingDataS: 数据正在加载时的状态。
    • LoadingDataErrorS: 数据加载时发生错误的状态。
  • 数据已加载状态(Data loaded states)

    • LoadedDataS: 数据成功加载或初始化成功的状态。
    • ReloadingDataS: 数据正在重新加载时的状态。
    • ReloadingDataErrorS: 数据重新加载时发生错误的状态。

错误处理

默认情况下,所有在数据加载期间抛出的错误都会转换为 DataException,可以在 LoadingDataErrorSReloadingDataErrorS 状态中接收。

如果你想在 BlocObserver.onError 中处理这些错误,可以通过覆盖 overridedOnLoadingErroroverridedOnReloadingError 方法来改变这种行为。

void _$onLoadingError(
  DataException error,
  UnloadedDataS<String> state,
  Emitter<DataS<String>> emit, {
  String? params,
}) {
  if (error is UnhandledDataException) {
    throw error;
  }
  emit(LoadingDataErrorS(error, params: params));
  emit(const UnloadedDataS());
}

void _$onReloadingError(
  DataException error,
  LoadedS<String, String> state,
  Emitter<DataS<String>> emit, {
  String? params,
}) {
  if (error is UnhandledDataException) {
    throw error;
  }
  emit(ReloadingDataErrorS(state, error, params: params));
  emit(LoadedDataS(state.data, params: state.params));
}

class AnimalBloc extends DataBloc<String, String> {
  AnimalBloc({
    required AnimalRepository animalRepository,
    super.overridedOnLoadingError = _$onLoadingError,
    super.overridedOnReloadingError = _$onReloadingError,
  })
}

然后你可以在 BlocObserver.onError 中处理它们:

[@override](/user/override)
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
  super.onError(bloc, error, stackTrace);
  if (error is UnhandledDataException) {
    final originError = error.error;
    final originStackTrace = error.stackTrace;
    // send to sentry...
  }
}

ListBloc 和 SelectBloc

ListBloc 提供了对列表进行排序和过滤的功能。而 SelectBloc 实现了从列表中选择项目的便捷操作。

示例代码

下面是一个完整的示例,展示了如何使用 bloc_toolkit 插件:

import 'dart:async';
import 'package:bloc_toolkit/bloc_toolkit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logger/logger.dart';

void main() {
  final logger = Logger();
  Bloc.observer = SimpleBlocObserver(logger);
  runApp(const MyApp());
}

class AnimalBloc extends DataBloc<String, String> {
  AnimalBloc({required AnimalRepository animalRepository})
      : _animalRepository = animalRepository;
  final AnimalRepository _animalRepository;

  [@override](/user/override)
  FutureOr<String> loadData(DataS<String> oldState, LoadDataE<String> event) {
    return _animalRepository.getAnimal(event.params!);
  }
}

class SimpleBlocObserver extends BlocObserver {
  SimpleBlocObserver(this._logger);

  final Logger _logger;

  [@override](/user/override)
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    final nextState = change.nextState;
    if (nextState is ErrorS) {
      final error = nextState.error;
      if (error is UnhandledDataException) {
        _logger.f('UnhandledDataException',
            error: error.error, stackTrace: error.stackTrace);
        //TODO: send to analytics
      }
    }
  }
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({
    super.key,
  });

  void _showSnackBar(BuildContext context, String text) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      backgroundColor: Colors.red,
      content: Text(text),
      duration: const Duration(milliseconds: 1000),
    ));
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: BlocProvider(
          create: (_) => AnimalBloc(animalRepository: AnimalRepository()),
          child: BlocConsumer<AnimalBloc, DataS<String>>(
            listener: (context, state) {
              if (state is ReloadingDataErrorS<String, String>) {
                _showSnackBar(
                    context, 'Reloading animal error: ${state.data}');
              } else if (state is LoadingDataErrorS<String, String>) {
                _showSnackBar(
                    context, 'Loading animal error: ${state.error}');
              }
            },
            builder: (context, state) {
              if (state is LoadingDataS<String>) {
                return const Text('Loading animal...');
              }
              if (state is UnloadedS<String>) {
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Text('Animal not loaded'),
                    ElevatedButton(
                      onPressed: () {
                        context
                            .read<AnimalBloc>()
                            .add(const LoadDataE(params: 'some args'));
                      },
                      child: const Text('Load Animal'),
                    ),
                  ],
                );
              }
              if (state is LoadedDataS<String, String>) {
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(state.data),
                    ElevatedButton(
                      onPressed: () {
                        context
                            .read<AnimalBloc>()
                            .add(const ReloadDataE(params: 'some args'));
                      },
                      child: const Text('Reload Animal'),
                    ),
                  ],
                );
              }
              if (state is ReloadingDataS<String, String>) {
                return const Text('Reloading animal...');
              }
              return const SizedBox();
            },
          ),
        ),
      ),
    );
  }
}

更多关于Flutter状态管理架构插件bloc_toolkit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter状态管理架构插件bloc_toolkit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用bloc_toolkit进行状态管理的代码示例。bloc_toolkit是一个集合了多个Bloc相关工具和扩展的Flutter包,它可以帮助我们更方便地进行状态管理。然而,需要注意的是,bloc_toolkit本身并不是状态管理架构的核心库,它更多地是对bloc库的增强。因此,我们通常还是使用bloc库来进行状态管理,而bloc_toolkit则提供一些便利的工具。

在这个示例中,我们将使用bloc库进行状态管理,并展示如何利用bloc_toolkit中的一些便利功能。

首先,确保在pubspec.yaml文件中添加必要的依赖:

dependencies:
  flutter:
    sdk: flutter
  bloc: ^8.0.0  # 请根据需要调整版本号
  flutter_bloc: ^8.0.0  # 请根据需要调整版本号
  bloc_toolkit: ^0.0.1  # 假设bloc_toolkit的版本号为0.0.1,请根据实际情况调整

然后,我们创建一个简单的计数器应用来展示如何使用这些库。

1. 创建CounterEvent类

import 'package:equatable/equatable.dart';

abstract class CounterEvent extends Equatable {
  const CounterEvent();

  @override
  List<Object> get props => [];
}

class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}

2. 创建CounterState类

import 'package:equatable/equatable.dart';

class CounterState extends Equatable {
  final int count;

  const CounterState({required this.count});

  @override
  List<Object> get props => [count];
}

3. 创建CounterBloc类

import 'package:bloc/bloc.dart';
import 'package:bloc_toolkit/bloc_toolkit.dart';  // 导入bloc_toolkit
import 'counter_event.dart';
import 'counter_state.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState(count: 0)) {
    on<IncrementEvent>((event, emit) => emit(state.copyWith(count: state.count + 1)));
    on<DecrementEvent>((event, emit) => emit(state.copyWith(count: state.count - 1)));
  }
}

在这个例子中,我们使用了bloc_toolkit,但实际上在这个简单的Bloc实现中并没有直接用到bloc_toolkit的特定功能。不过,bloc_toolkit可能提供了一些扩展函数或工具,可以简化Bloc的创建和管理。例如,如果bloc_toolkit提供了简化的Bloc创建方式或状态转换工具,我们可以在这里使用它们。但由于bloc_toolkit的具体API和功能可能随着版本而变化,这里我们主要展示基本的Bloc实现。

4. 使用CounterBloc在UI中

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

void main() {
  runApp(BlocProvider<CounterBloc>(
    create: (_) => CounterBloc(),
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
  }
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            BlocBuilder<CounterBloc, CounterState>(
              builder: (context, state) {
                return Text(
                  '${state.count}',
                  style: Theme.of(context).textTheme.headline4,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
          SizedBox(height: 10),
          FloatingActionButton(
            onPressed: () => context.read<CounterBloc>().add(DecrementEvent()),
            tooltip: 'Decrement',
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

在这个例子中,我们使用了BlocProvider来提供CounterBloc实例,并在UI中使用BlocBuilder来监听状态变化。同时,我们使用context.read<CounterBloc>().add(...)来分发事件。

请注意,这个示例中并没有直接使用bloc_toolkit的特定功能,因为bloc_toolkit通常提供的是对Bloc库的扩展和便利工具。如果bloc_toolkit提供了特定的功能,比如简化的Bloc创建、状态转换或调试工具,你可以根据bloc_toolkit的文档来集成这些功能。由于bloc_toolkit的具体API和功能可能随着版本而变化,请参考最新的bloc_toolkit文档以获取更多信息和示例。

回到顶部