Flutter状态管理增强插件warped_bloc的使用

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

Flutter状态管理增强插件warped_bloc的使用

介绍

[warped_bloc] 是一个用于减少在Flutter中使用BLoC模式时的样板代码的包。它基于Felix Angelov的[flutter_bloc]构建,专门用于减少API调用中的样板代码。

概念

每次使用像BLoC这样的状态管理解决方案处理API调用时,通常会有三个特定的状态:

  1. 加载状态 (Loading State):当请求正在处理时。
  2. 数据状态 (Data State):当请求成功并返回数据时。
  3. 错误状态 (Error State):当发生错误时。

这些状态在所有API调用中都是常见的,因此可以进行通用化处理。这就是[warped_bloc]诞生的原因。

如何使用?

[warped_bloc] 提供了预定义的状态类和一些辅助工具,帮助简化BLoC监听器和构建器的使用。

预定义的状态类
  • InitialState
  • LoadingState
  • ErrorState<T>
  • DataState<T>
辅助工具
  • defaultBuilder 函数:用于处理加载和错误状态的默认构建器。
  • defaultListener 函数:用于处理加载、错误和数据状态的默认监听器。
基础类
  • AsyncCubit 类:用于处理异步操作的基础类。
  • PaginatedAsyncCubit 类:用于处理分页数据的基础类。

示例代码

Get 请求示例
import 'package:example/repo/home_repo.dart';
import 'package:warped_bloc/warped_bloc.dart';

// 定义数据状态
class HomeLoaded extends DataState<List<String>> {
  const HomeLoaded(List<String> data) : super(data: data);
}

// 定义Cubit
class HomeCubit extends AsyncCubit {
  final HomeRepo repo;

  HomeCubit({
    required this.repo,
  });

  // 处理API请求
  fetch() {
    // handleDefaultStates自动处理加载和错误状态
    handleDefaultStates(() async {
      final data = await repo.fetch();
      emit(HomeLoaded(data));
    });
  }
}

// 定义Repository
class HomeRepo {
  final Dio dio;

  Future<List<String>> fetch() async {
    final res = await dio.get('/data');
    return List<String>.from(res.data);
  }
}

// UI处理
BlocBuilder<HomeCubit, BlocState>(
  bloc: cubit,
  builder: defaultBuilder<HomeLoaded, void>(
    onData: (context, state) {
      final data = state.data;
      return ListView.builder(
        itemCount: data.length,
        itemBuilder: (c, i) {
          var e = data[i];
          return Text(e);
        },
      );
    },
  ),
);
Post 请求示例
import 'package:warped_bloc/warped_bloc.dart';

// 定义成功状态
class HomeActionSuccess extends DataState<void> {
  const HomeActionSuccess() : super(data: null);
}

// 定义Cubit
class HomeActionCubit extends AsyncCubit {
  final HomeRepo repo;

  HomeActionCubit({required this.repo});

  // 处理API请求
  updateProfile(ProfileRequest request) {
    handleDefaultStates(() async {
      await repo.updateProfile(profile);
      emit(const HomeActionSuccess());
    });
  }
}

// 定义Repository
class HomeRepo {
  final Dio dio;

  Future<List<String>> updateProfile(ProfileRequest request) async {
    final res = await dio.post('/profile', data: request.toMap());
    return List<String>.from(res.data);
  }
}

// UI处理
final actionCubit = HomeActionCubit(repo: HomeRepo(dio: Dio()));
...
BlocListener<HomeActionCubit, BlocState>(
  listener: defaultListener<HomeActionSuccess, void>(),
  child: FloatingActionButton(
    onPressed: () {
      // actionCubit.someAction();
      actionCubit.someFailedAction();
    },
    child: const Icon(Icons.add),
  ),
);
...
分页处理示例
import 'package:example/repo/home_repo.dart';
import 'package:warped_bloc/warped_bloc.dart';

// 定义分页数据状态
class PaginatedHomeLoaded extends DataState<List<String>> {
  const PaginatedHomeLoaded({required List<String> data}) : super(data: data);
}

// 定义分页Cubit
class PaginatedHomeCubit extends PaginatedAsyncCubit<String> {
  final HomeRepo repo;

  PaginatedHomeCubit({
    required this.repo,
  });

  // 处理API请求
  void fetch() {
    handleDefaultStates(() async {
      final data = await paginatedFetch(() => repo.fetch(param: param));
      emit(PaginatedHomeLoaded(data: data));
    });
  }

  [@override](/user/override)
  void onFetchMore() {
    print('--- Fetch More');
    if (!hasNext) return;
    fetch();
  }
}

// 定义Repository
class HomeRepo {
  final Dio dio;

  Future<List<String>> fetch({PaginationParam param}) async {
    final res = await dio.get('/paginatedData', data: param.toMap());
    return List<String>.from(res.data);
  }
}

// 定义分页参数
class PaginationParam {
  final int page;
  final int perPage;

  PaginationParam({
    required this.page,
    required this.perPage,
  });

  Map<String, dynamic> toMap() {
    return {
      "page": page,
      "per_page": perPage,
    };
  }
}

// UI处理
BlocBuilder<PaginatedHomeCubit, BlocState>(
  builder: defaultBuilder<PaginatedHomeLoaded, void>(
    onData: (context, state) {
      final data = state.data;
      return PaginatedBuilder(
        builder: (c, controller) {
          return ListView.builder(
            controller: controller,
            itemCount: data.length,
            itemBuilder: (c, i) {
              var e = data[i];
              return ListTile(
                title: Text("${i + 1}$e"),
              );
            },
          );
        },
        onFetchMore: context.read<PaginatedHomeCubit>().onFetchMore,
      );
    },
  ),
)

完整示例Demo

以下是一个完整的示例应用,展示了如何使用warped_bloc进行状态管理和API调用。

import 'package:example/bloc/paginated_home_cubit.dart';
import 'package:example/repo/home_repo.dart';
import 'package:warped_bloc/cubit/pagination/paginated_builder.dart';
import 'package:warped_bloc/warped_bloc.dart';
import 'package:example/bloc/home_action_cubit.dart';
import 'package:example/bloc/home_cubit.dart';
import 'package:flutter/material.dart';

void main() {
  // 配置默认的加载和错误UI
  DefaultBuilderConfig.configure(
    onLoading: (context) {
      return const Center(
        child: Text("LOADING"),
      );
    },
    onError: (context, e) {
      return Column(
        children: [
          const Text("Errrrrrrorrr"),
          Text(e.message),
        ],
      );
    },
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: PaginatedHome(),
    );
  }
}

class Home extends StatefulWidget {
  [@override](/user/override)
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late HomeCubit cubit;
  late HomeActionCubit actionCubit;

  [@override](/user/override)
  void initState() {
    super.initState();
    cubit = HomeCubit(repo: HomeRepo())..fetch();
    actionCubit = HomeActionCubit();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // actionCubit.someAction();
          actionCubit.someFailedAction();
        },
        child: const Icon(Icons.add),
      ),
      body: BlocListener<HomeActionCubit, BlocState>(
        bloc: actionCubit,
        listener: defaultListener(onLoading: (c) {
          showLoadingDialog(context);
        }),
        child: BlocBuilder<HomeCubit, BlocState>(
          bloc: cubit,
          builder: defaultBuilder<HomeLoaded, String>(
            onData: (context, state) {
              final data = state.data;
              return ListView.builder(
                itemCount: data.length,
                itemBuilder: (c, i) {
                  var e = data[i];
                  return Text(e);
                },
              );
            },
          ),
        ),
      ),
    );
  }
}

class PaginatedHome extends StatelessWidget {
  const PaginatedHome({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (context) => PaginatedHomeCubit(repo: HomeRepo())..fetch(),
        ),
      ],
      child: _PaginatedHomeBody(),
    );
  }
}

class _PaginatedHomeBody extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: BlocBuilder<PaginatedHomeCubit, BlocState>(
        builder: defaultBuilder<PaginatedHomeLoaded, void>(
          onData: (context, state) {
            final data = state.data;
            return PaginatedBuilder(
              builder: (c, controller) {
                return ListView.builder(
                  controller: controller,
                  itemCount: data.length,
                  itemBuilder: (c, i) {
                    var e = data[i];
                    return ListTile(
                      title: Text("${i + 1}$e"),
                    );
                  },
                );
              },
              onFetchMore: context.read<PaginatedHomeCubit>().onFetchMore,
            );
          },
        ),
      ),
    );
  }
}

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

1 回复

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


当然,关于Flutter中的状态管理增强插件warped_bloc的使用,这里提供一个基本的代码案例来展示如何集成和使用这个插件。需要注意的是,warped_bloc是基于bloc库的一个扩展,它提供了一些额外的功能和灵活性。

首先,确保你的项目中已经添加了blocwarped_bloc的依赖。在pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  bloc: ^x.y.z  # 替换为最新版本号
  warped_bloc: ^x.y.z  # 替换为最新版本号

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

接下来,让我们创建一个简单的Flutter应用,演示如何使用warped_bloc进行状态管理。

1. 创建Bloc和Event

首先,我们需要定义一个Bloc和一个Event。在这个例子中,我们将创建一个简单的计数器Bloc

// counter_event.dart
import 'package:meta/meta.dart';

@immutable
abstract class CounterEvent {}

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(this.count);

  @override
  List<Object?> get props => [count];
}
// counter_bloc.dart
import 'package:bloc/bloc.dart';
import 'package:warped_bloc/warped_bloc.dart';
import 'counter_event.dart';
import 'counter_state.dart';

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

2. 创建UI组件

现在,我们创建一个简单的UI组件来显示计数器的状态并允许用户增加或减少计数。

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Counter App')),
        body: Center(
          child: BlocProvider(
            create: (context) => CounterBloc(),
            child: CounterView(),
          ),
        ),
        floatingActionButton: Column(
          mainAxisAlignment: MainAxisAlignment.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),
            ),
          ],
        ),
      ),
    );
  }
}

class CounterView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, CounterState>(
      builder: (context, state) {
        return Text('${state.count}');
      },
    );
  }
}

3. 运行应用

现在,你可以运行你的Flutter应用,应该会看到一个简单的计数器应用,其中包含一个显示当前计数的文本和一个增加/减少计数的浮动按钮。

这个示例展示了如何使用warped_bloc(通过继承WarpedBloc)来创建一个简单的计数器Bloc,并在UI中显示其状态。warped_bloc提供了额外的功能和灵活性,比如更复杂的错误处理和状态转换逻辑,你可以根据需要在你的应用中进一步利用这些功能。

回到顶部