Flutter网络数据请求与BLoC模式结合插件bloc_rest_api的使用

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

Flutter网络数据请求与BLoC模式结合插件bloc_rest_api的使用


bloc_rest_api简介

bloc_rest_api 是一个用于轻松集成和管理 REST API 的 Flutter 包。只需创建一个模型类,其余的工作都将由该包处理。

特性:

  • 支持所有平台(Android、iOS、Web、Linux、Windows、Mac)。
  • 集成 BLoC 模式,便于状态管理。
  • 提供多种方法来处理网络请求,包括 GET、POST 等。

使用步骤

1. 添加依赖

pubspec.yaml 文件中添加 bloc_rest_api 依赖:

dependencies:
  bloc_rest_api: <最新版本>

然后执行以下命令安装依赖:

# Dart
pub get

# Flutter
flutter pub get

2. 创建数据模型

首先,我们需要为要从互联网获取的数据创建一个模型类。可以使用工具如 QuickType 来快速生成模型类。

示例:创建一个 PostModel 类。

import 'package:equatable/equatable.dart';

class PostModel extends Equatable {
  PostModel({
    this.userId,
    this.id,
    this.title,
    this.body,
  });

  final int userId;
  final int id;
  final String title;
  final String body;

  factory PostModel.fromJson(Map<String, dynamic> json) => PostModel(
        userId: json['userId'] ?? null,
        id: json['id'] ?? null,
        title: json['title'] ?? null,
        body: json['body'] ?? null,
      );

  Map<String, dynamic> toJson() => {
        'userId': userId,
        'id': id,
        'title': title,
        'body': body,
      };

  [@override](/user/override)
  List<Object> get props => [userId, id];
}

3. 配置 RequestCubit

接下来,我们需要配置 RequestCubit 来处理网络请求。通过 BlocProvider 注册 RequestCubit,并传递 fromMap 方法以将 JSON 转换为模型对象。

MultiBlocProvider(
  providers: [
    // 单个模型的请求
    BlocProvider(
      create: (context) => RequestCubit<PostModel>(
        fromMap: (json) => PostModel.fromJson(json),
      ),
    ),
    // 列表模型的请求
    BlocProvider(
      create: (context) => RequestCubit<List<PostModel>>(
        fromMap: (json) =>
            List<PostModel>.from(json.map((x) => PostModel.fromJson(x))),
      ),
    ),
  ],
  child: AppView(),
);

4. 发起网络请求

使用 context.read() 调用 getRequestpostRequest 方法发起网络请求。

context.read<RequestCubit<PostModel>>().getRequest(
  baseUrl: 'https://jsonplaceholder.typicode.com/',
  handle: 'posts/1',
);

如果需要启用日志功能,可以通过设置 enableLogs 参数为 true

context.read<RequestCubit<PostModel>>().getRequest(
  baseUrl: 'https://jsonplaceholder.typicode.com/',
  handle: 'posts/1',
  enableLogs: true,
);

如果需要从本地 JSON 字符串中读取数据:

context.read<RequestCubit<PostModel>>().localRequest(
  'json string here',
);

5. 处理复杂数据

对于更复杂的场景,可以传递一个返回 Future<T> 的函数作为参数。

Future<PostModel> fetchAlbum() async {
  final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');

  if (response.statusCode == 200) {
    return PostModel.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to load album');
  }
}

// 调用
context.read<RequestCubit<PostModel>>().request(fetchAlbum);

6. 反应状态变化

使用 BlocBuilderBlocListenerBlocConsumer 监听状态变化,并根据状态更新 UI。

BlocBuilder<RequestCubit<PostModel>, RequestState<PostModel>>(
  builder: (context, state) {
    switch (state.status) {
      case RequestStatus.empty:
        return Center(child: Text('Press the button to get some data'));

      case RequestStatus.loading:
        return Center(child: CircularProgressIndicator());

      case RequestStatus.success:
        return Center(
          child: Padding(
            padding: const EdgeInsets.all(32.0),
            child: Text(state.model.toString()),
          ),
        );

      case RequestStatus.failure:
        return Center(child: Text(state.errorMessage));

      default:
        return Container();
    }
  },
);

全局配置

可以通过 ApiConfig 设置全局配置,例如基础 URL、请求头和超时时间。

ApiConfig.baseUrl = 'https://jsonplaceholder.typicode.com/';
ApiConfig.header = {'Content-Type': 'application/json'};
ApiConfig.responseTimeOut = Duration(seconds: 10);

持久化数据

如果需要持久化 API 数据,可以使用 HydratedRequestCubit 替换 RequestCubit,并提供 fromMaptoMap 方法。

BlocProvider(
  create: (context) => HydratedRequestCubit<PostModel>(
    fromMap: (json) => PostModel.fromJson(json),
    toMap: (model) => model.toJson(),
  ),
)

同时初始化存储:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  HydratedBloc.storage = await HydratedStorage.build();
  runApp(App());
}

完整示例代码

以下是一个完整的示例代码,展示了如何使用 bloc_rest_api 进行网络请求和状态管理。

import 'dart:convert';

import 'package:bloc_rest_api/bloc_rest_api.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:http/http.dart' as http;
import 'models/models.dart';

void main() {
  // 设置全局配置
  ApiConfig.baseUrl = "https://jsonplaceholder.typicode.com/";
  ApiConfig.header = {"Content-Type": "application/json"};

  runApp(App());
}

class App extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        // 单个模型的请求
        BlocProvider(
          create: (context) => RequestCubit<PostModel>(
            fromMap: (json) => PostModel.fromJson(json),
          ),
        ),
        // 列表模型的请求
        BlocProvider(
          create: (context) => RequestCubit<List<PostModel>>(
            fromMap: (json) =>
                List<PostModel>.from(json.map((x) => PostModel.fromJson(x))),
          ),
        ),
      ],
      child: AppView(),
    );
  }
}

class AppView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(title: 'Example App'),
    );
  }
}

class HomePage extends StatelessWidget {
  HomePage({Key key, this.title}) : super(key: key);

  final String title;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read<RequestCubit<PostModel>>().getRequest(
                handle: 'posts/1',
              );
        },
        child: Icon(Icons.add),
      ),
      body: BlocConsumer<RequestCubit<PostModel>, RequestState<PostModel>>(
        listener: (context, state) {
          if (state.status == RequestStatus.failure) {
            Scaffold.of(context).showSnackBar(
              SnackBar(content: Text(state.errorMessage)),
            );
          }
        },
        builder: (context, state) {
          switch (state.status) {
            case RequestStatus.empty:
              return Center(child: Text("Press the button to get some data"));

            case RequestStatus.loading:
              return Center(child: CircularProgressIndicator());

            case RequestStatus.success:
              return Center(
                child: Padding(
                  padding: const EdgeInsets.all(32.0),
                  child: Text(state.model.toString()),
                ),
              );

            case RequestStatus.failure:
              return Center(child: Text(state.errorMessage));

            default:
              return Container();
          }
        },
      ),
    );
  }
}

Future<PostModel> fetchAlbum() async {
  final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');

  if (response.statusCode == 200) {
    return PostModel.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to load album');
  }
}

更多关于Flutter网络数据请求与BLoC模式结合插件bloc_rest_api的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网络数据请求与BLoC模式结合插件bloc_rest_api的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,BLoC(Business Logic Component)模式是一种非常流行的状态管理方式,它可以帮助你将业务逻辑与UI层分离,使代码更加清晰和可维护。bloc_rest_api 是一个结合了BLoC模式和网络数据请求的插件,它可以帮助你更轻松地处理REST API请求。

1. 安装依赖

首先,你需要在 pubspec.yaml 文件中添加 blocbloc_rest_api 依赖:

dependencies:
  flutter:
    sdk: flutter
  bloc: ^8.0.0
  flutter_bloc: ^8.0.0
  bloc_rest_api: ^0.0.1

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

2. 创建BLoC

接下来,你可以创建一个BLoC来处理网络请求。假设你要从API获取用户数据,你可以创建一个 UserBloc

import 'package:bloc/bloc.dart';
import 'package:bloc_rest_api/bloc_rest_api.dart';
import 'package:equatable/equatable.dart';

part 'user_event.dart';
part 'user_state.dart';

class UserBloc extends Bloc<UserEvent, UserState> {
  final ApiClient apiClient;

  UserBloc({required this.apiClient}) : super(UserInitial());

  [@override](/user/override)
  Stream<UserState> mapEventToState(UserEvent event) async* {
    if (event is FetchUser) {
      yield UserLoading();
      try {
        final user = await apiClient.getUser(event.userId);
        yield UserLoaded(user);
      } catch (e) {
        yield UserError(e.toString());
      }
    }
  }
}

3. 定义事件和状态

你需要定义 UserEventUserState 来处理不同的事件和状态。

user_event.dart:

part of 'user_bloc.dart';

abstract class UserEvent extends Equatable {
  const UserEvent();

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

class FetchUser extends UserEvent {
  final String userId;

  const FetchUser(this.userId);

  [@override](/user/override)
  List<Object> get props => [userId];
}

user_state.dart:

part of 'user_bloc.dart';

abstract class UserState extends Equatable {
  const UserState();

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

class UserInitial extends UserState {}

class UserLoading extends UserState {}

class UserLoaded extends UserState {
  final User user;

  const UserLoaded(this.user);

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

class UserError extends UserState {
  final String message;

  const UserError(this.message);

  [@override](/user/override)
  List<Object> get props => [message];
}

4. 创建ApiClient

ApiClient 是一个用于处理网络请求的类。你可以使用 bloc_rest_api 提供的 RestApiClient 来简化网络请求。

import 'package:bloc_rest_api/bloc_rest_api.dart';

class ApiClient {
  final RestApiClient restApiClient;

  ApiClient({required this.restApiClient});

  Future<User> getUser(String userId) async {
    final response = await restApiClient.get('/users/$userId');
    return User.fromJson(response);
  }
}

5. 在UI中使用BLoC

最后,你可以在UI中使用 UserBloc 来获取和显示用户数据。

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

class UserScreen extends StatelessWidget {
  final String userId;

  const UserScreen({Key? key, required this.userId}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => UserBloc(apiClient: ApiClient(restApiClient: RestApiClient(baseUrl: 'https://api.example.com')))..add(FetchUser(userId)),
      child: Scaffold(
        appBar: AppBar(
          title: Text('User Details'),
        ),
        body: BlocBuilder<UserBloc, UserState>(
          builder: (context, state) {
            if (state is UserLoading) {
              return Center(child: CircularProgressIndicator());
            } else if (state is UserLoaded) {
              return Center(child: Text('User: ${state.user.name}'));
            } else if (state is UserError) {
              return Center(child: Text('Error: ${state.message}'));
            } else {
              return Center(child: Text('Initial State'));
            }
          },
        ),
      ),
    );
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!