Flutter列表管理插件list_bloc的使用

Flutter列表管理插件list_bloc的使用

概述

这个Flutter插件是一个全面的BLoC库,用于加载、管理和展示数据,并支持过滤和分页。它简化了处理各种数据状态(如加载、空、错误和已加载)的过程,使用了freezed包。

关键组件

  • DataCubit<T, F>:管理具有泛型类型的数据状态(数据类型为T,过滤器类型为F)。
  • ListCubit<T, F>:专门用于处理数据列表,继承自DataCubit
  • PaginatedCubit<T, F>:专门为分页数据处理设计,继承自DataCubit
  • ContinousListBloc<T, F>:继承自ListCubit,用于实现连续列表,需要在过滤器类中包含OffsetLimitFilter

安装

在你的Flutter项目的pubspec.yaml文件中添加以下依赖:

dependencies:
  list_bloc: ^最新版本号

使用

基本使用 DataCubit

final dataCubit = DataCubit<YourDataType, YourFilterType>(yourDataLoaderFunction);

// 加载数据
dataCubit.load();

// 清除数据
dataCubit.clear();

使用 ListCubit 进行列表管理

final listCubit = ListCubit<YourListItemType, YourFilterType>(yourListLoaderFunction);

// 重新加载整个列表
listCubit.reload();

// 向现有列表追加更多项目
listCubit.append();

// 在本地添加或删除项目
listCubit.addLocally(yourItem);
listCubit.removeLocally(yourItem);

使用 PaginatedCubit 处理分页数据

final paginatedCubit = PaginatedCubit<YourPaginatedItemType, YourFilterType>(yourPaginatedLoaderFunction);

// 使用方式类似于 DataCubit,但专为分页数据设计。

高级用法示例

import 'package:list_bloc.dart';

class ItemFilter {
  enum Type { fruits, vegetables }
}

class Item {
  int type;
  int value;
}

typedef FruitBloc = ListCubit<Item, ItemFilter>;
typedef FruitState = Data<List<Item>, ItemFilter>;

Future<List<Item>> loader([ItemFilter? filter]) async => api.....

void main() {
  final bloc = FruitBloc(loader);

  bloc.load(ItemFilter.Type.fruits);

  BlocBuilder<FruitBloc, FruitState>(
    bloc: bloc,
    builder: (context, state) {
      for (var item in state.data) {
        // 处理你的数据
      }
    },
  );
}

功能与问题

我们欢迎你对这个插件提出反馈和贡献。请在问题跟踪器中提交功能请求和bug报告。


示例代码

以下是一个完整的示例代码,展示了如何使用ListCubit来管理一个简单的列表。

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

typedef _ItemCubit = ListCubit<_Item, _ItemType>;
typedef _ItemState = Data<List<_Item>, _ItemType>;

void main() {
  runApp(const ExampleApp());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(useMaterial3: true),
      home: BlocProvider<_ItemCubit>(
        create: (context) => ListCubit<_Item, _ItemType>(
          ([_ItemType? filter]) async {
            final items = [
              const _Item(type: _ItemType.fruit, value: 'Apple'),
              const _Item(type: _ItemType.vegetable, value: 'Potato'),
            ].where((item) {
              if (filter == null) return true;
              return item.type == filter;
            }).toList();

            return items;
          },
        ),
        child: const _ListBlocPage(),
      ),
    );
  }
}

class _ListBlocPage extends StatelessWidget {
  const _ListBlocPage();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ListBloc 示例'),
        actions: [
          IconButton(
            onPressed: () => context.read<_ItemCubit>().reload(),
            icon: const Icon(Icons.refresh),
          ),
        ],
      ),
      body: BlocBuilder<_ItemCubit, _ItemState>(
        builder: (context, state) {
          if (state is Empty) {
            return const Center(
              child: Text('列表中没有项目'),
            );
          }

          if (state is Loading) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          if (state is Error) {
            return Center(
              child: Text('错误: ${state.error}'),
            );
          }

          final items = state.data ?? [];

          return ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              final item = items[index];

              return ListTile(
                  title: Text(item.value),
                  subtitle: Text(item.type.name),
                  trailing: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      IconButton(
                        onPressed: () async {
                          final updatedItem = await showDialog<_Item>(
                            context: context,
                            builder: (context) => _UpdateItemDialogContent(
                              item: item,
                              itemType: item.type,
                            ),
                            barrierDismissible: false,
                          );
                          if (updatedItem != null && context.mounted) {
                            context.read<_ItemCubit>().replaceLocally(
                                  (oldItem) => oldItem == item,
                                  updatedItem,
                                );
                          }
                        },
                        icon: const Icon(Icons.edit),
                      ),
                      IconButton(
                        onPressed: () {
                          context.read<_ItemCubit>().removeLocally(item);
                        },
                        icon: const Icon(Icons.delete),
                      ),
                    ],
                  ));
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          final item = await showDialog<_Item>(
            context: context,
            builder: (context) => const _UpdateItemDialogContent(),
            barrierDismissible: false,
          );
          if (item != null && context.mounted) {
            context.read<_ItemCubit>().addLocally(item);
          }
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

class _UpdateItemDialogContent extends StatefulWidget {
  final _Item? item;
  final _ItemType? itemType;

  const _UpdateItemDialogContent({
    Key? key,
    this.item,
    this.itemType,
  }) : super(key: key);

  [@override](/user/override)
  State<_UpdateItemDialogContent> createState() => _UpdateItemDialogContentState();
}

class _UpdateItemDialogContentState extends State<_UpdateItemDialogContent> {
  final TextEditingController _itemController = TextEditingController();
  _ItemType? _itemType;

  _ItemType? get itemType => _itemType;

  set itemType(_ItemType? value) {
    setState(() {
      _itemType = value;
    });
  }

  [@override](/user/override)
  void initState() {
    _itemController.text = widget.item?.value ?? '';
    _itemType = widget.itemType;
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('${widget.item == null ? '添加' : '编辑'}项目'),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextField(
            controller: _itemController,
            decoration: const InputDecoration(labelText: '项目值'),
            onTapOutside: (_) => FocusScope.of(context).unfocus(),
          ),
          const SizedBox(height: 16),
          DropdownButton<_ItemType>(
            value: _itemType,
            hint: const Text('项目类型'),
            onChanged: (value) => _itemType = value,
            isExpanded: true,
            items: _ItemType.values
                .map(
                  (e) => DropdownMenuItem<_ItemType>(
                    value: e,
                    child: Text(e.name),
                  ),
                )
                .toList(),
          ),
        ],
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.of(context).pop(),
          child: const Text('取消'),
        ),
        const SizedBox(width: 16),
        ElevatedButton(
          onPressed: _itemType == null || _itemController.text.trim().isEmpty
              ? null
              : () {
                  final item = _Item(
                    type: _itemType ?? _ItemType.fruit,
                    value: _itemController.text,
                  );
                  Navigator.of(context).pop(item);
                },
          child: const Text('保存'),
        ),
      ],
    );
  }
}

enum _ItemType { fruit, vegetable }

class _Item {
  const _Item({
    required this.type,
    required this.value,
  });

  final _ItemType type;
  final String value;
}

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用list_bloc插件来管理列表状态的示例代码。list_bloc是一个状态管理库,用于处理列表数据的增删改查操作。在这个例子中,我们假设你已经有一个Flutter项目,并且已经添加了flutter_bloclist_bloc依赖。

首先,确保你的pubspec.yaml文件中包含以下依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.0.0  # 请根据最新版本调整
  list_bloc: ^0.1.0  # 假设这是list_bloc的假设版本号,请根据实际情况调整

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

接下来,我们创建一个简单的列表管理示例。

1. 定义Item

首先,定义一个简单的Item类来表示列表中的每一项:

class Item {
  final String id;
  final String title;

  Item({required this.id, required this.title});
}

2. 创建ListEvent, ListState, 和 ListBloc

接下来,我们创建事件、状态和Bloc。

ListEvent

import 'package:equatable/equatable.dart';

abstract class ListEvent extends Equatable {
  const ListEvent();

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

class ItemAdded extends ListEvent {
  final Item item;

  ItemAdded({required this.item});

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

class ItemRemoved extends ListEvent {
  final String id;

  ItemRemoved({required this.id});

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

ListState

import 'package:equatable/equatable.dart';
import 'package:list_bloc/list_bloc.dart'; // 假设list_bloc提供了ListState基类

class MyListState extends ListState<Item> {
  const MyListState({required List<Item> items}) : super(items: items);

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

注意:如果list_bloc没有提供ListState基类,你需要自己实现一个状态类,如下所示:

class MyListState extends Equatable {
  final List<Item> items;

  const MyListState({required this.items});

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

ListBloc

import 'package:bloc/bloc.dart';
import 'package:list_bloc/list_bloc.dart'; // 假设list_bloc提供了ListBloc基类
import 'list_event.dart';
import 'list_state.dart';

class MyListBloc extends ListBloc<Item, ListEvent, MyListState> {
  MyListBloc() : super(const MyListState(items: []));

  @override
  Stream<MyListState> mapEventToState(ListEvent event) async* {
    if (event is ItemAdded) {
      yield* _addItem(event.item);
    } else if (event is ItemRemoved) {
      yield* _removeItem(event.id);
    }
  }
}

注意:如果list_bloc没有提供ListBloc基类,你需要自己实现Bloc逻辑,如下所示:

class MyListBloc extends Bloc<ListEvent, MyListState> {
  MyListBloc() : super(const MyListState(items: []));

  @override
  Stream<MyListState> mapEventToState(ListEvent event) async* {
    if (event is ItemAdded) {
      final currentState = state;
      yield MyListState(items: List.from(currentState.items)..add(event.item));
    } else if (event is ItemRemoved) {
      final currentState = state;
      yield MyListState(items: currentState.items
          .where((item) => item.id != event.id)
          .toList());
    }
  }
}

3. 使用BlocProvider和BlocBuilder

最后,在你的UI中使用BlocProviderBlocBuilder来显示和管理列表。

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:your_app/bloc/list_bloc.dart'; // 替换为你的实际路径
import 'package:your_app/models/item.dart'; // 替换为你的实际路径

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('List Management')),
        body: ListPage(),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            final textEditingController = TextEditingController();
            showDialog(
              context: context,
              builder: (context) => AlertDialog(
                title: Text('Add Item'),
                content: TextField(
                  controller: textEditingController,
                ),
                actions: [
                  TextButton(
                    onPressed: () => Navigator.of(context).pop(),
                    child: Text('Cancel'),
                  ),
                  TextButton(
                    onPressed: () {
                      final newItem = Item(
                        id: Uuid().v4(), // 使用Uuid库生成唯一ID
                        title: textEditingController.text,
                      );
                      context.read<MyListBloc>().add(ItemAdded(item: newItem));
                      Navigator.of(context).pop();
                    },
                    child: Text('Add'),
                  ),
                ],
              ),
            );
          },
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

class ListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<MyListBloc, MyListState>(
      builder: (context, state) {
        return ListView.builder(
          itemCount: state.items.length,
          itemBuilder: (context, index) {
            final item = state.items[index];
            return Dismissible(
              key: Key(item.id),
              onDismissed: (direction) {
                context.read<MyListBloc>().add(ItemRemoved(id: item.id));
              },
              child: ListTile(
                title: Text(item.title),
              ),
            );
          },
        );
      },
    );
  }
}

注意:上面的代码中使用了Uuid库来生成唯一ID,你需要添加uuid依赖并在代码中导入它。

dependencies:
  uuid: ^3.0.4  # 请根据最新版本调整
import 'package:uuid/uuid.dart';

这个示例展示了如何使用list_bloc(或手动实现的等效功能)来管理Flutter应用中的列表状态。你可以根据需要进一步扩展和修改这个示例。

回到顶部