Flutter数据持久化插件flutter_data的使用

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

Flutter数据持久化插件flutter_data的使用

Flutter Data 是一个强大的数据框架,旨在为 Flutter 应用提供零样板代码的持久化和反应式模型。本文将介绍如何使用 flutter_data 插件进行数据持久化,并提供完整的示例 Demo。

特性

  • 离线优先:基于 Hive 的本地存储,支持失败处理和重试 API。
  • 直观的 API 和简单的设置:通过 Dart mixins 和代码生成器实现可配置和可组合。
  • 强大的关系支持:自动同步的关系图和反应式关系。

快速开始

设置和初始化

首先,在 pubspec.yaml 文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_data: ^<latest_version>
  riverpod: ^<latest_version>

dev_dependencies:
  build_runner: ^<latest_version>
  json_serializable: ^<latest_version>

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

flutter pub get

接下来,创建一个模型类并使用 @DataRepository 注解:

import 'package:flutter_data/flutter_data.dart';
import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart'; // 这个文件会自动生成

@JsonSerializable()
@DataRepository([MyJSONServerAdapter])
class User extends DataModel<User> {
  @override
  final int? id; // ID 可以是任何类型
  final String name;

  User({this.id, required this.name});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

mixin MyJSONServerAdapter on RemoteAdapter<User> {
  @override
  String get baseUrl => "https://my-json-server.typicode.com/flutterdata/demo/";
}

运行代码生成器:

flutter pub run build_runner build

使用 Repository

在 Widget 中使用 ref.users 来访问 Repository:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'user.dart';

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(usersProvider.watchOne(1));

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter Data Example')),
        body: Center(
          child: state.when(
            data: (user) => Text(user!.name),
            loading: () => CircularProgressIndicator(),
            error: (err, stack) => Text('Error: $err'),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () async {
            await ref.read(usersProvider).save(User(id: 1, name: 'Updated'));
          },
          child: Icon(Icons.update),
        ),
      ),
    );
  }
}

更多示例

创建新用户

final user = await User(name: 'New User').save();
print(user.id); // 新用户的ID

查找用户

final user = await ref.users.findOne(1, params: {'_embed': 'tasks'});
print(user.tasks.length); // 用户的任务数量

删除任务

await user.tasks.last.delete();

完整示例 Demo

以下是完整示例 Demo,包含基本的增删改查操作:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_data/flutter_data.dart';
import 'models/user.dart';

const List<int> _encryptionKey = [
  146, 54, 40, 58, 46, 90, 152, 2, 193, 210, 220, 199, 16, 96, 107, 4,
  243, 133, 171, 31, 241, 26, 149, 53, 172, 36, 121, 103, 17, 155, 120, 61
];

void main() {
  runApp(ProviderScope(child: MyApp()));
}

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

class HomeScreen extends ConsumerStatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends ConsumerState<HomeScreen> {
  late Directory _dir;

  @override
  void initState() {
    super.initState();
    initStorage();
  }

  Future<void> initStorage() async {
    final container = ProviderContainer(
      overrides: [
        configureRepositoryLocalStorage(
          baseDirFn: () => _dir.path,
          encryptionKey: _encryptionKey,
          clear: LocalStorageClearStrategy.always,
        ),
      ],
    );

    _dir = Directory.systemTemp.createTempSync();
    print('Using temporary directory: ${_dir.path}');
    _dir.deleteSync(recursive: true);

    await container.read(repositoryInitializerProvider.future);

    container.users.logLevel = 2;
    container.tasks.logLevel = 2;

    await container.tasks.findAll(params: {'user_id': 1, '_limit': 3});

    final user = User(id: 19, name: 'Zeku');
    final user2 = await container.users.findOne(19, remote: false);

    assert(user == user2);
    assert(user.tasks.length == 3);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter Data Example')),
      body: Center(
        child: Text('Check console for logs.'),
      ),
    );
  }

  @override
  void dispose() {
    _dir.deleteSync(recursive: true);
    super.dispose();
  }
}

通过上述步骤,您可以轻松地在 Flutter 应用中集成 flutter_data 插件,实现数据持久化和离线优先的功能。更多详细信息请参考 官方文档


更多关于Flutter数据持久化插件flutter_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据持久化插件flutter_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 flutter_data 插件进行 Flutter 数据持久化的代码示例。flutter_data 是一个强大的 Flutter 库,它简化了从远程 API 获取数据并将其本地持久化到设备存储(通常是 SQLite)的过程。

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

dependencies:
  flutter:
    sdk: flutter
  flutter_data: ^0.x.y  # 请替换为最新版本号
  json_annotation: ^4.0.0  # flutter_data 通常依赖于此

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

接下来,设置你的数据模型。假设我们有一个简单的用户模型:

import 'package:flutter_data/flutter_data.dart';
import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@DataModel()
class User with DataModelMixin {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  // 从 JSON 反序列化
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  // 序列化到 JSON
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

// 生成 DataModel 相关的代码
@generateDataModel(classes: [User])

然后,你需要创建一个数据仓库(DataRepository),它定义了如何远程获取数据和本地存储数据。flutter_data 通常与 hivesembast 一起使用进行本地存储,但这里我们主要关注它的远程数据获取和本地缓存功能。

创建一个配置类来设置你的远程 API 端点:

import 'package:flutter_data/flutter_data.dart';
import 'package:http/http.dart' as http;

class MyRemoteAdapter extends RemoteAdapter<User> {
  @override
  String get baseUrl => 'https://jsonplaceholder.typicode.com'; // 示例 API

  @override
  Future<List<User>> fetchAll(DataQuery<User> query) async {
    final response = await http.get(Uri.parse('$baseUrl/users'));
    if (response.statusCode == 200) {
      final List<dynamic> body = jsonDecode(response.body);
      return body.map((dynamic item) => User.fromJson(item)).toList();
    } else {
      throw Exception('Failed to load users');
    }
  }

  @override
  Future<User?> fetchOne(int id) async {
    final response = await http.get(Uri.parse('$baseUrl/users/$id'));
    if (response.statusCode == 200) {
      final Map<String, dynamic> body = jsonDecode(response.body);
      return User.fromJson(body);
    } else {
      throw Exception('Failed to load user $id');
    }
  }
}

在你的应用中配置 flutter_data

import 'package:flutter/material.dart';
import 'package:flutter_data/flutter_data.dart';
import 'user.dart'; // 导入你的数据模型
import 'my_remote_adapter.dart'; // 导入你的远程适配器

void main() {
  runApp(
    DataRepositoryProvider(
      repos: [
        DataRepository<User>(
          adapter: MyRemoteAdapter(),
          // 可选:配置本地存储,这里使用内存存储作为示例
          localAdapter: MemoryDataAdapter<User>(),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter Data Example')),
        body: UserListScreen(),
      ),
    );
  }
}

最后,创建一个屏幕来显示用户列表:

import 'package:flutter/material.dart';
import 'package:flutter_data/flutter_data.dart';
import 'user.dart';

class UserListScreen extends StatefulWidget {
  @override
  _UserListScreenState createState() => _UserListScreenState();
}

class _UserListScreenState extends State<UserListScreen> {
  late final DataRepository<User> usersRepository;

  @override
  void initState() {
    super.initState();
    usersRepository = DataRepositoryProvider.of<User>(context)!;
    // 预加载所有用户
    usersRepository.watchAll();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('User List')),
      body: FutureBuilder<List<User>>(
        future: usersRepository.findAll(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error!}'));
          } else {
            final users = snapshot.data ?? [];
            return ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) {
                final user = users[index];
                return ListTile(
                  title: Text(user.name),
                  subtitle: Text(user.email),
                );
              },
            );
          }
        },
      ),
    );
  }
}

这个示例展示了如何使用 flutter_data 插件从远程 API 获取用户数据并将其本地持久化。请注意,实际项目中你可能需要更复杂的错误处理和状态管理。此外,根据你的需求,你可能还需要配置本地存储适配器(如 HiveDataAdapterSembastDataAdapter)来替代内存存储。

回到顶部