Flutter响应式状态管理插件rx_bloc_list的使用
Flutter响应式状态管理插件rx_bloc_list的使用
rx_bloc_list简介
rx_bloc_list
包简化了在Flutter应用中实现无限滚动和下拉刷新功能。它旨在与RxBloc生态系统一起使用。
使用方法
添加依赖
在pubspec.yaml
文件中添加依赖:
dependencies:
rx_bloc_list: any
导入包
确保在项目中导入该包:
import 'package:rx_bloc_list/rx_bloc_list.dart';
示例代码
以下是一个完整的示例,演示如何使用rx_bloc_list
来创建一个带有无限滚动和下拉刷新功能的列表页面:
主入口
import 'package:flutter/material.dart';
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
import 'package:rx_bloc/rx_bloc.dart';
import 'package:rx_bloc_list/rx_bloc_list.dart';
import 'package:rxdart/rxdart.dart';
void main() {
runApp(const MyApp());
}
/// App entry
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'RxBlocList Example',
home: RxBlocProvider<UserBlocType>(
create: (context) => UserBloc(repository: UserRepository()),
child: const PaginatedListPage(),
),
);
}
}
分页列表页面
/// region Paginated List page
class PaginatedListPage extends StatelessWidget {
const PaginatedListPage({super.key});
@override
Widget build(BuildContext context) => Scaffold(
body: SafeArea(
child: RxPaginatedBuilder<UserBlocType, User>.withRefreshIndicator(
state: (bloc) => bloc.states.paginatedList,
onBottomScrolled: (bloc) => bloc.events.loadPage(),
onRefresh: (bloc) async {
bloc.events.loadPage(reset: true);
return bloc.states.refreshDone;
},
buildSuccess: (context, list, bloc) => ListView.builder(
itemBuilder: (context, index) {
final user = list.getItem(index);
if (user == null) {
return const YourProgressIndicator();
}
return YourListTile(user: user);
},
itemCount: list.itemCount,
),
buildLoading: (context, list, bloc) =>
const YourProgressIndicator(),
buildError: (context, list, bloc) =>
YourErrorWidget(error: list.error!),
),
),
);
}
/// App specific list tile
class YourListTile extends StatelessWidget {
/// Default constructor
const YourListTile({
required this.user,
super.key,
});
/// The model
final User user;
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: CircleAvatar(
child: Text(user.id.toString()),
),
title: Text(user.name),
),
);
}
}
/// App specific error widget
class YourErrorWidget extends StatelessWidget {
/// Default constructor
const YourErrorWidget({
required this.error,
super.key,
});
/// The error presented in the widget
final Exception error;
@override
Widget build(BuildContext context) => Text(error.toString());
}
/// App specific progress indicator
class YourProgressIndicator extends StatelessWidget {
/// Default constructor
const YourProgressIndicator({
super.key,
});
@override
Widget build(BuildContext context) => const Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CircularProgressIndicator(),
),
);
}
/// endregion
用户BLoC
/// region User Bloc
/// A contract class containing all events of the UserBloC.
abstract class UserBlocEvents {
/// Load the next page of data. If reset is true, refresh the data and load
/// the very first page
void loadPage({bool reset = false});
}
/// A contract class containing all states of the UserBloC.
abstract class UserBlocStates {
/// The loading state
Stream<bool> get isLoading;
/// The error state
Stream<String> get errors;
/// The paginated list data
Stream<PaginatedList<User>> get paginatedList;
/// Returns when the data refreshing has completed
@RxBlocIgnoreState()
Future<void> get refreshDone;
}
/// User Bloc
@RxBloc()
class UserBloc extends $UserBloc {
/// UserBloc default constructor
UserBloc({required UserRepository repository}) {
_$loadPageEvent
// Start the data fetching immediately when the page loads
.startWith(true)
.fetchData(repository, _paginatedList)
// Enable state handling by the current bloc
.setResultStateHandler(this)
// Merge the data in the _paginatedList
.mergeWithPaginatedList(_paginatedList)
.bind(_paginatedList)
// Make sure we dispose the subscription
.addTo(_compositeSubscription);
}
/// Internal paginated list stream
final _paginatedList = BehaviorSubject<PaginatedList<User>>.seeded(
PaginatedList<User>(
list: [],
pageSize: 50,
),
);
@override
Future<void> get refreshDone async => _paginatedList.waitToLoad();
@override
Stream<PaginatedList<User>> _mapToPaginatedListState() => _paginatedList;
@override
Stream<String> _mapToErrorsState() =>
errorState.map((error) => error.toString());
@override
Stream<bool> _mapToIsLoadingState() => loadingState;
/// Disposes of all streams to prevent memory leaks
@override
void dispose() {
_paginatedList.close();
super.dispose();
}
}
/// Utility extensions for the Stream<bool> streams used within User Bloc
extension UserBlocStreamExtensions on Stream<bool> {
/// Fetches appropriate data from the repository
Stream<Result<PaginatedList<User>>> fetchData(
UserRepository repository,
BehaviorSubject<PaginatedList<User>> paginatedList,
) =>
switchMap(
(reset) {
if (reset) paginatedList.value.reset();
return repository
.fetchPage(
paginatedList.value.pageNumber + 1,
paginatedList.value.pageSize,
)
.asResultStream();
},
);
}
/// endregion
模型和仓库
/// region Models and repositories
class User {
/// User constructor
User({
required this.id,
this.name = '',
});
/// The id of the user
final int id;
/// The name of the user
final String name;
}
/// User repository which represents a mock repository which simulates retrieval
/// of mock data, adding a custom delay.
class UserRepository {
/// Fetches a specific page from the repository with the given size
Future<PaginatedList<User>> fetchPage(int page, int pageSize) async {
await Future.delayed(const Duration(seconds: 2));
if (page > 10) {
return PaginatedList(
list: [],
pageSize: pageSize,
);
}
return PaginatedList(
list: List.generate(
pageSize,
(index) {
final realIndex = ((page - 1) * pageSize) + index;
return User(
id: realIndex,
name: 'User #$realIndex',
);
},
),
pageSize: pageSize,
);
}
}
/// endregion
生成的代码
/// region Generated code
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// Generator: RxBlocGeneratorForAnnotation
// **************************************************************************
/// Used as a contractor for the bloc, events and states classes
/// @nodoc
abstract class UserBlocType extends RxBlocTypeBase {
/// Events of the bloc
UserBlocEvents get events;
/// States of the bloc
UserBlocStates get states;
}
/// [$UserBloc] extended by the [UserBloc]
/// @nodoc
abstract class $UserBloc extends RxBlocBase
implements UserBlocEvents, UserBlocStates, UserBlocType {
final _compositeSubscription = CompositeSubscription();
/// Тhe [Subject] where events sink to by calling [loadPage]
final _$loadPageEvent = PublishSubject<bool>();
/// The state of [isLoading] implemented in [_mapToIsLoadingState]
late final Stream<bool> _isLoadingState = _mapToIsLoadingState();
/// The state of [errors] implemented in [_mapToErrorsState]
late final Stream<String> _errorsState = _mapToErrorsState();
/// The state of [paginatedList] implemented in [_mapToPaginatedListState]
late final Stream<PaginatedList<User>> _paginatedListState =
_mapToPaginatedListState();
@override
void loadPage({bool reset = false}) => _$loadPageEvent.add(reset);
@override
Stream<bool> get isLoading => _isLoadingState;
@override
Stream<String> get errors => _errorsState;
@override
Stream<PaginatedList<User>> get paginatedList => _paginatedListState;
Stream<bool> _mapToIsLoadingState();
Stream<String> _mapToErrorsState();
Stream<PaginatedList<User>> _mapToPaginatedListState();
@override
UserBlocEvents get events => this;
@override
UserBlocStates get states => this;
@override
void dispose() {
_$loadPageEvent.close();
_compositeSubscription.dispose();
super.dispose();
}
}
/// endregion
通过以上代码,你可以创建一个包含无限滚动和下拉刷新功能的分页列表页面。希望这个示例能帮助你更好地理解和使用rx_bloc_list
插件。如果你有任何问题或需要进一步的帮助,请随时提问!
更多关于Flutter响应式状态管理插件rx_bloc_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter响应式状态管理插件rx_bloc_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用 rx_bloc_list
插件进行 Flutter 响应式状态管理的代码示例。rx_bloc_list
结合了 flutter_bloc
和 get_it
,并利用 rxdart
库提供的响应式编程功能来管理列表状态。
首先,确保你的 pubspec.yaml
文件中包含以下依赖:
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.0.0
get_it: ^7.2.0
rxdart: ^0.27.2
rx_bloc_list: ^0.2.0 # 请注意版本号,根据实际情况调整
然后运行 flutter pub get
来获取这些依赖。
1. 设置 GetIt 容器
在你的项目根目录创建一个 dependency_injection.dart
文件来设置 GetIt 容器:
import 'package:get_it/get_it.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rx_bloc_list/rx_bloc_list.dart';
final GetIt sl = GetIt.instance;
void setupLocator() {
sl.registerLazySingleton<MyBloc>(() => MyBloc());
}
在你的 main.dart
文件中初始化 GetIt 容器:
import 'package:flutter/material.dart';
import 'dependency_injection.dart';
void main() {
setupLocator();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
2. 创建 Model 和 State
创建一个简单的 Model 类,例如 Item
:
class Item {
final String id;
final String name;
Item({required this.id, required this.name});
}
3. 创建 Bloc 和 Event/State
创建一个 MyEvent
类来定义事件:
import 'package:equatable/equatable.dart';
abstract class MyEvent extends Equatable {
const MyEvent();
}
class FetchItemsEvent extends MyEvent {
@override
List<Object?> get props => [];
}
创建 MyState
类来定义状态:
import 'package:equatable/equatable.dart';
import 'package:rx_bloc_list/rx_bloc_list.dart';
import 'item.dart';
abstract class MyState extends Equatable {
const MyState();
}
class MyInitialState extends MyState {
@override
List<Object?> get props => [];
}
class MyLoadedState extends MyState with RxListState<Item> {
MyLoadedState({required List<Item> items}) : super(items: items);
@override
List<Object?> get props => [items];
}
4. 创建 Bloc
创建 MyBloc
类来处理事件并生成状态:
import 'package:bloc/bloc.dart';
import 'package:rxdart/rxdart.dart';
import 'my_event.dart';
import 'my_state.dart';
import 'item.dart';
class MyBloc extends Bloc<MyEvent, MyState> {
MyBloc() : super(MyInitialState()) {
on<FetchItemsEvent>((event, emit) async {
// 模拟从服务器获取数据
final items = List.generate(10, (index) => Item(id: index.toString(), name: 'Item $index'));
emit(MyLoadedState(items: items));
});
}
}
5. 使用 RxBlocListBuilder 构建 UI
在你的 MyHomePage
中使用 RxBlocListBuilder
来构建响应式列表:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rx_bloc_list/rx_bloc_list.dart';
import 'my_bloc.dart';
import 'item.dart';
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Rx Bloc List Example'),
),
body: BlocProvider(
create: (context) => sl<MyBloc>(),
child: BlocListener<MyBloc, MyState>(
listener: (context, state) {},
child: RxBlocListBuilder<MyBloc, MyState, Item>(
bloc: context.read<MyBloc>(),
itemBuilder: (context, item, index) {
return ListTile(
title: Text(item.name),
);
},
emptyWidget: Center(child: Text('No items found')),
loadingWidget: Center(child: CircularProgressIndicator()),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<MyBloc>().add(FetchItemsEvent());
},
tooltip: 'Fetch Items',
child: Icon(Icons.add),
),
);
}
}
总结
以上代码展示了如何使用 rx_bloc_list
插件来管理 Flutter 应用中的响应式列表状态。通过定义事件、状态和 Bloc 类,并使用 RxBlocListBuilder
来构建 UI,你可以实现一个简洁且响应式的列表管理解决方案。