Flutter服务管理插件flutter_service_bloc的使用

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

Flutter服务管理插件flutter_service_bloc的使用

flutter_service_bloc 是一个用于在Flutter项目中实现服务层与Bloc架构集成的包。它可以帮助开发者更方便地管理服务请求、响应和错误处理。本文将详细介绍如何使用 flutter_service_bloc,并提供一个完整的示例Demo。

1. 使用方法

1.1 Bloc

flutter_service_bloc 的核心是 ServiceBloc,它继承自 Bloc,专门用于处理服务层的逻辑。你可以通过 ServiceBloc 来发起服务请求,并根据请求的结果(成功、失败、加载中)来更新UI。关于 Bloc 的详细用法,请参考 service_bloc

1.2 Listener

ServiceBlocListener 用于监听 ServiceBloc 的状态变化,并根据不同的状态执行相应的操作。常见的使用场景包括显示加载对话框、导航到其他页面或与其他 Bloc 进行通信。

  • Dialog:当服务请求处于加载状态时显示加载对话框,请求成功或失败后关闭对话框。
ServiceBlocListener<ProductCheckoutServiceBloc, ProductCheckoutServiceRequested, String>(
  onLoading: (context, state, event) => showLoading(),
  onResponded: (context, state, event) => hideLoading(),
  onFailed: (context, state, event, error) => showErrorDialog(context, error),
),
  • Navigate:当服务请求成功后,导航到指定的页面。
ServiceBlocListener<ProductCheckoutServiceBloc, ProductCheckoutServiceRequested, String>(
  onSucceed: (context, state, event, data) => Navigator.of(context).pushNamed('/your_route'),
),
  • Bloc通信:当某个服务请求成功后,触发其他 Bloc 的事件。例如,用户登出后清除用户数据。
ServiceBlocListener<UserLogoutServiceBloc, UserLogoutServiceRequested, dynamic>(
  onSucceed: (context, state, event, data) => context.read<UserCubit>().clearUserData(),
),
1.3 Builder

ServiceBlocBuilder 用于构建UI,它会根据 ServiceBloc 的状态返回不同的Widget。你可以为加载中、成功和失败分别定义不同的UI。

ServiceBlocBuilder<OpenLibraryAuthorDetailServiceBloc, OpenLibraryAuthorDetailServiceRequested, OpenLibraryAuthorDetail>(
  onLoading: (context, state, event) => // your loading widget,
  onSucceed: (context, state, event, data) {
    return // your success widget
  },
  onFailed: (context, state, event, error) => // your failure widget,
),

2. 完整示例Demo

下面是一个完整的示例,展示了如何使用 flutter_service_bloc 来实现一个简单的书籍作者搜索和详情展示功能。这个示例包括了 ServiceBloc 的创建、ServiceBlocListenerServiceBlocBuilder 的使用。

2.1 主应用入口
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_service_bloc/flutter_service_bloc.dart';
import 'open_library/repository/repository.dart';

void main() {
  runApp(MultiRepositoryProvider(
    providers: [
      RepositoryProvider(
        create: (context) => OpenLibraryRepository(),
      )
    ],
    child: const MyApp(),
  ));
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomePage(),
      onGenerateRoute: (settings) {
        late final Widget page;
        switch (settings.name) {
          case HomePage.routeName:
            page = const HomePage();
            break;

          case OpenLibraryAuthorSearchPage.routeName:
            page = BlocProvider(
              create: (context) => OpenLibraryAuthorSearchServiceBloc(
                  context.read<OpenLibraryRepository>()),
              child: const OpenLibraryAuthorSearchPage(),
            );
            break;

          case OpenLibraryAuthorDetailPage.routeName:
            assert(settings.arguments is OpenLibraryAuthorDetailPageParameter);
            final parameter =
                settings.arguments as OpenLibraryAuthorDetailPageParameter;
            page = BlocProvider(
              create: (context) => OpenLibraryAuthorDetailServiceBloc(
                  context.read<OpenLibraryRepository>())
                ..add(OpenLibraryAuthorDetailServiceRequested(parameter.key)),
              child: OpenLibraryAuthorDetailPage(parameter: parameter),
            );
            break;

          default:
            throw UnimplementedError('page name not found');
        }

        return MaterialPageRoute(
          builder: (context) => page,
          settings: settings,
        );
      },
    );
  }
}
2.2 主页
class HomePage extends StatelessWidget {
  static const routeName = '/home';

  const HomePage({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('flutter_service_bloc example'),
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              ElevatedButton(
                onPressed: () =>
                    Navigator.of(context).pushNamed(OpenLibraryAuthorSearchPage.routeName),
                child: const Text('Open Library API'),
              )
            ],
          ),
        ),
      ),
    );
  }
}
2.3 作者搜索页面
class OpenLibraryAuthorSearchPage extends StatefulWidget {
  static const routeName = '/openLibraryAuthorSearch';

  const OpenLibraryAuthorSearchPage({super.key});

  [@override](/user/override)
  State<OpenLibraryAuthorSearchPage> createState() =>
      _OpenLibraryAuthorSearchPageState();
}

class _OpenLibraryAuthorSearchPageState extends State<OpenLibraryAuthorSearchPage> {
  late final TextEditingController _textEditingController;
  late final ScrollController _scrollController;

  [@override](/user/override)
  void initState() {
    super.initState();
    final serviceBloc = context.read<OpenLibraryAuthorSearchServiceBloc>();

    _textEditingController = TextEditingController();
    _textEditingController.addListener(() {
      // 搜索框内容变化时重新加载数据
      serviceBloc.add(OpenLibraryAuthorSearchReloadServiceRequested(_textEditingController.text));
    });

    _scrollController = ScrollController();
    _scrollController.addListener(() {
      // 当滚动到底部时加载更多数据
      if (_scrollController.position.maxScrollExtent - _scrollController.position.pixels <= 144.0 &&
          serviceBloc.pagination.hasNextPage) {
        serviceBloc.add(OpenLibraryAuthorSearchServiceRequested(_textEditingController.text));
      }
    });
  }

  [@override](/user/override)
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Author Search'),
        actions: [
          ServiceBlocBuilder<
              OpenLibraryAuthorSearchServiceBloc,
              OpenLibraryAuthorSearchServiceRequested,
              List<OpenLibraryAuthorSearchResult>>(
            onLoading: (context, state, event) => const Padding(
              padding: EdgeInsets.all(8.0),
              child: Center(
                child: CircularProgressIndicator(
                  color: Colors.white,
                ),
              ),
            ),
            onSuccess: (context, state, event, response) => const SizedBox(),
            onFailure: (context, state, event, error) => const SizedBox(),
          ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: [
            TextField(
              controller: _textEditingController,
              decoration: const InputDecoration(
                hintText: 'Search author',
                prefixIcon: Icon(Icons.search),
              ),
            ),
            Expanded(
              child: ServiceBlocBuilder<
                  OpenLibraryAuthorSearchServiceBloc,
                  OpenLibraryAuthorSearchServiceRequested,
                  List<OpenLibraryAuthorSearchResult>>(
                onSuccess: (context, state, event, response) {
                  return ListView.builder(
                    controller: _scrollController,
                    itemBuilder: (context, index) {
                      final author = response[index];
                      return ListTile(
                        title: Text(author.name),
                        onTap: () => Navigator.of(context).pushNamed(
                            OpenLibraryAuthorDetailPage.routeName,
                            arguments: OpenLibraryAuthorDetailPageParameter(
                                key: author.key, name: author.name)),
                      );
                    },
                    itemCount: response.length,
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}
2.4 作者详情页面
class OpenLibraryAuthorDetailPageParameter extends Equatable {
  const OpenLibraryAuthorDetailPageParameter({
    required this.key,
    required this.name,
  });

  final String key;
  final String name;

  [@override](/user/override)
  List<Object?> get props => [key, name];
}

class OpenLibraryAuthorDetailPage extends StatelessWidget {
  static const routeName = '/openLibraryAuthorDetail';

  const OpenLibraryAuthorDetailPage({super.key, required this.parameter});

  final OpenLibraryAuthorDetailPageParameter parameter;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(parameter.name)),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ServiceBlocBuilder<OpenLibraryAuthorDetailServiceBloc,
            OpenLibraryAuthorDetailServiceRequested, OpenLibraryAuthorDetail>(
          onLoading: (context, state, event) =>
              const Center(child: CircularProgressIndicator()),
          onSuccess: (context, state, event, response) {
            return ListView(
              children: [
                if (response.photoIdList.isNotEmpty) ...[
                  SizedBox(
                    height: 320,
                    child: PageView.builder(
                      itemBuilder: (context, index) {
                        final photoId = response.photoIdList[index];
                        return Image.network(
                          'https://covers.openlibrary.org/a/id/$photoId.jpg',
                          errorBuilder: (context, error, stackTrace) =>
                              const Placeholder(color: Colors.red),
                        );
                      },
                      itemCount: response.photoIdList.length,
                    ),
                  ),
                  const Divider(),
                ],
                Text('Name: ${response.name}'),
                const Divider(),
                if (response.personalName != null) ...[
                  Text('Personal Name: ${response.personalName}'),
                  const Divider(),
                ],
                if (response.alternateNameList.isNotEmpty) ...[
                  Text(
                      'Alternate names: ${response.alternateNameList.take(10).join(', ')}'),
                  const Divider(),
                ],
                if (response.title != null) ...[
                  Text('Title: ${response.title}'),
                  const Divider(),
                ],
                if (response.birthDate != null) ...[
                  Text('Birthday: ${response.birthDate}'),
                  const Divider(),
                ],
                if (response.wikipedia != null)
                  Text('Wikipedia: ${response.wikipedia}'),
              ],
            );
          },
        ),
      ),
    );
  }
}

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

1 回复

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


当然,以下是一个关于如何使用 flutter_service_bloc 插件来管理 Flutter 应用中的服务的代码示例。flutter_service_bloc 是一个用于在 Flutter 应用中管理和协调后台服务的插件,它结合了 BLoC(Business Logic Component)设计模式来更好地组织代码。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_service_bloc 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_service_bloc: ^最新版本号

然后运行 flutter pub get 来获取依赖。

接下来,我们创建一个简单的服务和一个 BLoC 来管理这个服务。

1. 创建服务类

首先,定义一个简单的服务类,比如一个计时服务:

import 'dart:async';

class TimerService {
  Timer? _timer;
  int _counter = 0;

  StreamController<int> _controller = StreamController<int>();

  Stream<int> get counterStream => _controller.stream;

  void start() {
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      _counter++;
      _controller.add(_counter);
    });
  }

  void stop() {
    _timer?.cancel();
    _controller.close();
  }

  void dispose() {
    _controller.close();
  }
}

2. 创建 BLoC 类

接下来,创建一个 BLoC 类来管理 TimerService

import 'package:bloc/bloc.dart';
import 'package:flutter_service_bloc/flutter_service_bloc.dart';
import 'package:meta/meta.dart';
import 'timer_service.dart';

part 'timer_event.dart';
part 'timer_state.dart';

class TimerBloc extends Bloc<TimerEvent, TimerState> with ServiceBloc<TimerService> {
  TimerBloc() : super(TimerInitial()) {
    on<TimerStarted>((event, emit) async {
      service = TimerService();
      service!.start();
      service!.counterStream.listen((counter) {
        add(TimerTicked(counter));
      });
      emit(TimerRunning());
    });

    on<TimerStopped>((event, emit) async {
      service?.stop();
      service?.dispose();
      emit(TimerStopped());
    });

    on<TimerTicked>((event, emit) {
      emit(TimerRunning(counter: event.counter));
    });

    on<TimerReset>((_, emit) async {
      service?.stop();
      service?.dispose();
      service = TimerService();
      emit(TimerInitial());
    });

    @override
    Future<void> close() {
      service?.dispose();
      return super.close();
    }
  }

  @override
  TimerService? createService() => TimerService();
}

3. 定义事件和状态类

timer_event.dart 文件中定义事件:

part of 'timer_bloc.dart';

abstract class TimerEvent {}

class TimerStarted extends TimerEvent {}

class TimerStopped extends TimerEvent {}

class TimerTicked extends TimerEvent {
  final int counter;

  TimerTicked(this.counter);
}

class TimerReset extends TimerEvent {}

timer_state.dart 文件中定义状态:

part of 'timer_bloc.dart';

abstract class TimerState {}

class TimerInitial extends TimerState {}

class TimerRunning extends TimerState {
  final int? counter;

  TimerRunning({this.counter});
}

class TimerStopped extends TimerState {}

4. 使用 BLoC 和服务

最后,在你的 Flutter 组件中使用 TimerBloc

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:your_app/bloc/timer_bloc.dart';

void main() {
  runApp(MultiBlocProvider(
    providers: [
      BlocProvider<TimerBloc>(
        create: (context) => TimerBloc(),
      ),
    ],
    child: MyApp(),
  ));
}

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

class TimerPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Timer')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BlocBuilder<TimerBloc, TimerState>(
              builder: (context, state) {
                if (state is TimerRunning) {
                  return Text('Counter: ${state.counter ?? 0}');
                } else if (state is TimerStopped || state is TimerInitial) {
                  return Text('Timer is stopped');
                }
                return Container();
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                context.read<TimerBloc>().add(TimerStarted());
              },
              child: Text('Start'),
            ),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                context.read<TimerBloc>().add(TimerStopped());
              },
              child: Text('Stop'),
            ),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                context.read<TimerBloc>().add(TimerReset());
              },
              child: Text('Reset'),
            ),
          ],
        ),
      ),
    );
  }
}

以上代码展示了如何使用 flutter_service_bloc 插件来管理一个简单的计时服务。你可以根据实际需求扩展服务和 BLoC 的功能。

回到顶部