Flutter状态管理简化插件bloc_ease的使用

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

Flutter状态管理简化插件bloc_ease的使用

bloc_ease 是一个Dart库,旨在通过使用typedefs代替定义状态类来解决 flutter_bloc 中样板代码的问题。以下是关于如何使用 bloc_ease 的详细指南。

目录

问题及解决方案

解决的问题

  1. 重复编写相同类型的状态:对于每个 Bloc/Cubit(初始、加载、成功、失败)。
  2. 重写 ==hashCode:或使用 Equatable 包为所有状态。
  3. 处理不需要的所有状态:即使只需要成功状态。
  4. 返回相同的Widget:例如 ProgressIndicator 用于加载状态。
  5. 管理 buildWhen:以避免处理所有状态。
  6. 采用不良实践:如使用单个状态类而不是继承。
  7. 管理多个状态:由于样板代码的存在。

提供的解决方案

  1. 消除编写状态类的需求:利用泛型(如 SucceedState<Auth> vs SucceedState<User>)。
  2. 全局处理常见状态:在UI中自动处理初始、加载和失败状态。
  3. 提供构建器:以类型安全的方式提供成功对象,并自主处理其他状态。
  4. 使用typedefs:轻松区分状态(如 typedef AuthSucceedState = SucceedState<Auth>),并提供了IntelliJ和VSCode的代码片段。

如何使用

步骤1:配置BlocEaseStateWidgetProvider

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

  @override
  Widget build(BuildContext context) {
    return BlocEaseStateWidgetProvider(
      initialStateBuilder: () => const Placeholder(),
      loadingStateBuilder: ([progress]) => const Center(child: CircularProgressIndicator()),
      failureStateBuilder: ([exceptionObject, failureMessage]) => Center(child: Text(failureMessage ?? 'Oops something went wrong!')),
      child: MaterialApp(
        //...
      ),
    );
  }
}

步骤2:创建Bloc/Cubit

使用快捷键 bloceaseblocbloceasecubit 创建基于需求的Bloc或Cubit。此模板需要编辑两个名称:

  • Cubit名称 -> UserCubit
  • 成功对象 -> User(这是从Bloc/Cubit的成功状态预期的对象)
import 'package:bloc_ease/bloc_ease.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

typedef UserState = BlocEaseState<User>; // Success Object

typedef UserInitialState = InitialState<User>;
typedef UserLoadingState = LoadingState<User>;
typedef UserSucceedState = SucceedState<User>;
typedef UserFailedState = FailedState<User>;

typedef UserBlocBuilder = BlocBuilder<UserCubit, UserState>;
typedef UserBlocListener = BlocListener<UserCubit, UserState>;
typedef UserBlocConsumer = BlocConsumer<UserCubit, UserState>;

typedef UserBlocEaseBuilder = BlocEaseStateBuilder<UserCubit, User>;
typedef UserBlocEaseListener = BlocEaseStateListener<UserCubit, User>;
typedef UserBlocEaseConsumer = BlocEaseStateConsumer<UserCubit, User>;

class UserCubit extends Cubit<UserState> {
  UserCubit(this.userRepo) : super(const UserInitialState());

  final UserRepo userRepo;

  void fetchUser() async {
    emit(const UserLoadingState());

    try {
      final user = await userRepo.fetchUser();
      emit(UserSucceedState(user));
    } catch (e) {
      emit(UserFailedState('Failed to fetch user', e));
    }
  }
}

步骤3:在UI中使用<CubitName>BlocEaseBuilder

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

  @override
  Widget build(BuildContext context) {
    return UserBlocEaseBuilder(
      succeedBuilder: (user) => SomeOtherWidget(user),
    );
  }
}

示例代码

获取用户详情

获取用户通常需要4种状态:

  • 初始状态 - 当未登录时
  • 加载状态 - 当获取进行中
  • 成功状态 - 当成功获取时
  • 失败状态 - 用户不可用或获取失败
// 定义Cubit
class UserCubit extends Cubit<UserState> {
  UserCubit(this.userRepo) : super(const UserInitialState());

  final UserRepo userRepo;

  void fetchUser() async {
    emit(const UserLoadingState());

    try {
      final user = await userRepo.fetchUser();
      emit(UserSucceedState(user));
    } catch (e) {
      emit(UserFailedState('Failed to fetch user', e));
    }
  }
}

// UI部分
class UserDetailPage extends StatelessWidget {
  const UserDetailPage({super.key});

  @override
  Widget build(BuildContext context) {
    return UserBlocEaseBuilder(
      succeedBuilder: (user) => UserDetailsWidget(user),
    );
  }
}

模板

IntelliJ 和 Android Studio

复制模板后,在IntelliJ/Android studio设置中添加Live Templates,创建新的模板组为BlocEase并粘贴模板。

VSCode

复制模板后,在VSCode中按 Cmd(Ctrl) + Shift + P,选择 “Snippets: Configure User Snippets”,然后在dart.json中粘贴。

技巧与窍门

使用BlocEaseListener和BlocEaseConsumer

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

  @override
  Widget build(BuildContext context) {
    return UserBlocEaseListener(
      succeedListener: (user) {
        // 处理成功状态
      },
      child: // 子组件
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return UserBlocEaseConsumer(
      succeedBuilder: (user) => SomeWidget(user),
      child: // 子组件
    );
  }
}

测试

blocTest<UserCubit, UserState>(
  'emits UserSucceedState after fetching user',
  setUp: () {
    when(repo.fetchUser).thenAnswer((_) async => mockUser);
  },
  build: () => UserCubit(repository: repo),
  act: (cubit) => cubit.fetchUser(),
  expect: () => UserSucceedState(mockUser),
  verify: (_) => verify(repository.fetchUser).called(1),
);

以上是关于 bloc_ease 插件的详细介绍和使用方法,希望对您有所帮助!如果有任何问题或建议,请随时联系我。


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

1 回复

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


当然,以下是一个关于如何在Flutter项目中使用bloc_ease插件来简化状态管理的代码案例。bloc_ease是一个旨在简化Bloc状态管理库使用的辅助插件。下面是一个简单的示例,展示如何集成和使用bloc_ease

1. 添加依赖

首先,在你的pubspec.yaml文件中添加blocbloc_ease的依赖:

dependencies:
  flutter:
    sdk: flutter
  bloc: ^8.0.0
  flutter_bloc: ^8.0.0
  bloc_ease: ^0.1.0  # 确保版本号是最新的

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

2. 创建Bloc和Event

假设我们有一个简单的计数器应用,我们需要一个计数器事件和一个计数器状态。

counter_event.dart

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 {}

counter_state.dart

import 'package:equatable/equatable.dart';

class CounterState extends Equatable {
  final int count;

  const CounterState({required this.count});

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

  CounterState copyWith({int? count}) {
    return CounterState(count: count ?? this.count);
  }
}

3. 创建Bloc

使用bloc_ease来简化Bloc的创建。

counter_bloc.dart

import 'package:bloc/bloc.dart';
import 'package:bloc_ease/bloc_ease.dart';
import 'counter_event.dart';
import 'counter_state.dart';

part 'counter_bloc.g.dart';

@Cubit(states: [CounterState], initialState: CounterState(count: 0))
class CounterBloc extends _$CounterBloc with _$CounterBlocEaseMixin {
  @override
  void on<T extends CounterEvent>(_Event event) {
    return when(
      event is IncrementEvent, () => emit(state.copyWith(count: state.count + 1)),
      event is DecrementEvent, () => emit(state.copyWith(count: state.count - 1)),
    );
  }
}

运行flutter pub run build_runner build来生成counter_bloc.g.dart文件。

4. 使用BlocProvider

在你的主应用文件中使用BlocProvider来提供Bloc实例。

main.dart

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

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

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

5. 创建UI组件

counter_page.dart

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

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            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(
        mainAxisAlignment: MainAxisAlignment.end,
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          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),
          ),
        ],
      ),
    );
  }
}

总结

以上代码展示了如何使用bloc_ease来简化Bloc状态管理。通过@Cubit注解和_$BlocEaseMixin,我们可以更方便地定义和处理Bloc事件和状态。希望这个示例能帮助你更好地理解如何在Flutter项目中使用bloc_ease

回到顶部