Flutter数据处理与功能封装插件functional_data的使用

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

Flutter数据处理与功能封装插件functional_data的使用

functional_data 是一个简单的、非侵入式的代码生成器,用于为数据类型生成样板代码。该包为每个类生成一个简单的mixin,其中包含 operator==hashCodecopyWithtoString 以及lenses。

Boiler plate(样板代码)

由于样板代码是作为一个mixin生成的,因此对类的接口影响最小。你只需要提供一个带有所有字段命名参数的构造函数,并扩展生成的mixin即可。

@FunctionalData()
class Person extends _$Person {
  final String name;
  final int age;

  const Person({this.name, this.age});

  const Person.anonymous() : this(name: "John Doe", age: null);

  int get ageInDays => 356 * age;
}

这种设计使你完全控制类。例如,你可以像平时一样添加命名构造函数或方法。

使用

要使用 functional_data,请将以下依赖项添加到你的包中:

dependencies:
  functional_data:

dev_dependencies:
  build_runner:
  functional_data_generator:

然后运行 flutter packages pub run build_runner build lib 来生成代码。

Lenses(透镜)

对于每个类,都会为所有字段生成透镜,允许查看字段或创建具有某种方式修改的字段的新实例。例如,Personname 的透镜是 Person$.name。要将透镜聚焦于特定实例,请使用其 of 方法:

final teacher = Person(name: "Arthur", age: 53);

print(Person$.name.of(teacher).value);
// -> "Arthur"

print(Person$.age.of(teacher).update(60));
// -> Person(name: "Arthur", age: 60)

print(Person$.name.of(teacher).map((name) => name.toUpperCase()));
// -> Person(name: "ARTHUR", age: 53)

当组合透镜时,它的强大之处就显现出来了。它允许你轻松地创建一个大型嵌套数据结构的副本,并修改其中一个叶节点的字段。两个透镜可以使用 then 进行链式调用。

class Course extends _$Course {
  final String name;
  final List<Person> students;

  const Course({this.students});
}

final programming = Course(
  name: "Programming 101",
  students: [
    Person(name: "Jane", age: 21),
    Person(name: "Tom", age: 20)
  ]
);

final firstStudentsName = Course$.students.then(List$.first<Person>()).then(Person$.name);

print(firstStudentsName.of(programming).update("Marcy"));
// -> Course(students: [Person(name: "Marcy", age: 21), Person(name: "Tom", age: 20)])

相比之下,不使用透镜的方式如下:

final firstStudent = programming.students.first;
final updatedFirstStudent = Person(name: "Marcy", age: firstStudent.age);
final updatedStudents = [updatedFirstStudent] + programming.students.skip(1);
final updatedCourse = Course(name: programming.name, students: updatedStudents);

这种方式更难以阅读且容易出错。想象一下当其中一个类增加了一个字段时会发生什么。

完整示例

// lens.dart
import 'package:collection/collection.dart';
import 'package:functional_data/functional_data.dart';

part 'lens.g.dart';

@FunctionalData()
class Foo extends _$Foo {
  final int number;
  final String name;

  const Foo({this.number, this.name});
}

@FunctionalData()
class Bar extends _$Bar {
  final Foo foo;

  @CustomEquality(DeepCollectionEquality())
  final List<Foo> foos;

  final String driver;

  const Bar({this.foo, this.foos, this.driver});
}

void main() {
  final foo = Foo(number: 42, name: "Marvin");
  final bar = Bar(
    foo: foo,
    foos: [
      Foo(number: 101, name: "One"),
      Foo(number: 102, name: "Two")
    ],
    driver: "One"
  );

  final fooName = Bar$.foo.then(Foo$.name);
  print(fooName.of(bar).map((name) => name.toUpperCase()));
  // Bar(foo: Foo(number: 42, name: MARVIN), foos: [Foo(number: 101, name: One), Foo(number: 102, name: Two)], driver: One)

  final firstFooName = Bar$.foos.then(List$.atIndex<Foo>(0)).then(Foo$.name);
  print(firstFooName.of(bar).update("Twee"));
  // Bar(foo: Foo(number: 42, name: Marvin), foos: [Foo(number: 101, name: Twee), Foo(number: 102, name: Two)], driver: One)

  final nameOfFooNamedTwo = Bar$.foos.then(List$.where<Foo>((foo) => foo.name == "Two")).then(Foo$.name);
  print(nameOfFooNamedTwo.update(bar, "Due"));
  // Bar(foo: Foo(number: 42, name: Marvin), foos: [Foo(number: 101, name: One), Foo(number: 102, name: Due)], driver: One)

  final driversNumber = Bar$.foos.thenWithContext((bar) => List$.where<Foo>((foo) => foo.name == bar.driver).then(Foo$.number));
  print(driversNumber.of(bar).value);
  // 101
}

代码生成配置

类级别配置

可以通过向 @FunctionalData 发送参数来指定应为类生成哪些功能。

@FunctionalData(
  generateCopy: false,
  generateLenses: false,
)
class Foo extends _$Foo {}

项目级别配置

可以通过在项目的根目录下创建一个名为 functional_data_options.yaml 的文件来指定应为整个项目生成哪些功能。类级别的参数将覆盖项目级别的配置。

generateCopyWith: false
generateCopyUsing: false
generateLenses: false

通过这些配置,你可以灵活地控制生成的代码,以满足项目的需求。


更多关于Flutter数据处理与功能封装插件functional_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据处理与功能封装插件functional_data的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中,使用functional_data插件可以简化数据处理和功能封装。尽管这个插件可能不是官方库的一部分,但假设它是一个自定义或第三方库,其设计目的通常是为了更好地组织和管理数据逻辑。以下是一个示例,展示如何在Flutter项目中使用假设的functional_data插件进行数据处理和功能封装。

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

dependencies:
  flutter:
    sdk: flutter
  functional_data: ^x.y.z  # 替换为实际版本号

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

接下来,让我们编写一些代码来演示如何使用这个插件。假设functional_data提供了一些用于数据处理和封装的实用函数或类。

1. 定义数据模型

假设我们有一个简单的用户数据模型:

// models/user.dart
import 'package:functional_data/functional_data.dart';  // 假设这是插件的导入路径

class User extends Equatable {
  final String id;
  final String name;
  final int age;

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

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

2. 使用functional_data进行数据处理

假设functional_data提供了一个DataProcessor类,用于处理数据逻辑。下面是如何使用它的一个示例:

// utils/data_processor.dart
import 'package:functional_data/functional_data.dart';  // 假设这是插件的导入路径
import 'models/user.dart';

class UserDataProcessor {
  // 假设DataProcessor是functional_data提供的一个类
  final DataProcessor<User> processor = DataProcessor<User>();

  // 示例:获取用户列表并应用一些转换
  List<User> fetchAndProcessUsers() {
    // 模拟从API获取用户数据
    List<Map<String, dynamic>> rawUsers = [
      {'id': '1', 'name': 'Alice', 'age': 30},
      {'id': '2', 'name': 'Bob', 'age': 25},
    ];

    // 使用DataProcessor的map函数转换数据
    return processor.map(rawUsers, (raw) {
      return User(
        id: raw['id']!,
        name: raw['name']!,
        age: raw['age']!,
      );
    });
  }
}

3. 在UI中使用处理后的数据

最后,我们在Flutter的UI组件中使用这些处理后的数据:

// pages/user_list.dart
import 'package:flutter/material.dart';
import 'utils/data_processor.dart';
import 'models/user.dart';

class UserListPage extends StatefulWidget {
  @override
  _UserListPageState createState() => _UserListPageState();
}

class _UserListPageState extends State<UserListPage> {
  late List<User> users;
  final UserDataProcessor processor = UserDataProcessor();

  @override
  void initState() {
    super.initState();
    users = processor.fetchAndProcessUsers();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('User List'),
      ),
      body: ListView.builder(
        itemCount: users.length,
        itemBuilder: (context, index) {
          User user = users[index];
          return ListTile(
            title: Text(user.name),
            subtitle: Text('Age: ${user.age}'),
          );
        },
      ),
    );
  }
}

在这个示例中,我们:

  1. 定义了一个用户数据模型User
  2. 使用functional_data提供的DataProcessor类(假设存在)来处理用户数据。
  3. 在Flutter的UI组件UserListPage中显示处理后的用户列表。

请注意,上述代码是基于假设的functional_data插件的功能和API设计的。实际使用时,你需要根据functional_data插件的文档和API来调整代码。如果functional_data插件的API与上述示例不同,请参考其官方文档进行实现。

回到顶部