Flutter动画列表插件implicitly_animated_list的使用

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

Flutter动画列表插件implicitly_animated_list的使用

在Flutter开发中,implicitly_animated_list 插件提供了一种优雅的方式来创建带有过渡动画的列表。它可以在数据变化时自动为插入、删除或更新的项目添加动画效果。下面将详细介绍如何使用这个插件。

基本用法

ImplicitlyAnimatedList 是一个可以接收任意类型数据列表,并根据这些数据构建Widget的组件。当传入的数据发生变化时(例如:添加、移除或修改),它会以动画的形式展示这些变更。最简单的例子如下:

ImplicitlyAnimatedList(
  itemData: [1, 2, 3, 4], // 数据源
  itemBuilder: (_, number) => ListTile(title: Text('$number')), // 构建每个item的widget
)

这段代码展示了如何基于一个整数列表来创建一个带动画的列表。每当您更改此列表中的元素并进行热重载时,列表将会以动画形式展现变化。

高级用法

结合 Stream 使用

除了静态的数据列表外,implicitly_animated_list 还非常适合与 StreamBuilder 搭配使用,以便实时响应数据流的变化。这里有一个使用用户模型作为示例的片段:

class User {
  const User({required this.firstName, required this.lastName});

  final String firstName;
  final String lastName;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is User &&
          runtimeType == other.runtimeType &&
          firstName == other.firstName &&
          lastName == other.lastName;

  @override
  int get hashCode => firstName.hashCode ^ lastName.hashCode;
}

...

StreamBuilder<List<User>>(
  stream: someSource.usersStream,
  builder: (context, snapshot) {
    if (!snapshot.hasData) return Center(child: CircularProgressIndicator());

    return ImplicitlyAnimatedList(
      itemData: snapshot.data!,
      itemBuilder: (context, user) {
        return ListTile(title: Text('${user.firstName} ${user.lastName}'));
      },
    );
  },
);

在这个例子中,我们定义了一个 User 类,并实现了它的 ==hashCode 方法,这样 ImplicitlyAnimatedList 就能够正确识别哪些项是相同的,从而只对实际发生变化的部分应用动画。

自定义比较逻辑

有时你可能需要自定义判断两个项目是否相同的方式。通过设置 itemEquality 参数,你可以提供一个函数来自定义这项行为。比如上面的例子中,我们可以选择只比较用户的姓氏来决定是否为同一人:

itemEquality: (a, b) => a.lastName == b.lastName,

使用 Sliver 版本

如果你的应用程序中已经有了 CustomScrollView 或者你需要更复杂的滚动控制,那么可以考虑使用 SliverImplicitlyAnimatedList,它提供了类似的功能但适用于 Sliver 环境:

CustomScrollView(
  slivers: [
    SliverImplicitlyAnimatedList(
      itemData: myListOfItems,
      itemBuilder: (context, item) => ListTile(title: Text('$item')),
    ),
  ],
),

完整示例

为了更好地理解如何使用 implicitly_animated_list,下面给出一个完整的示例应用程序代码,该程序会生成随机数字并显示在一个可动画的列表中:

import 'dart:math';

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

void main() => runApp(MyApp());

class MyItem {
  const MyItem({required this.value, required this.version});

  final int value;
  final int version;

  @override
  bool operator ==(Object other) =>
      other is MyItem && this.value == other.value && this.version == other.version;

  @override
  int get hashCode => Object.hash(value, version);

  @override
  String toString() => '$value (version: $version)';
}

List<MyItem> updateItems(List<MyItem> old) {
  return List.generate(
    10,
    (i) => MyItem(
      value: Random().nextInt(3),
      version: i < old.length ? old[i].version + 1 : 0,
    ),
  );
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  var _items = updateItems([]);
  var _initialAnimation = true;
  var _customEquality = true;
  var _resetKey = ValueKey(0);

  bool _myCustomEquality(MyItem a, MyItem b) => a.value == b.value;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Implicitly animated list'),
          actions: [_buildSettingsButton()],
        ),
        floatingActionButton: FloatingActionButton.extended(
          label: Text('Generate numbers'),
          icon: Icon(Icons.directions_walk),
          onPressed: () => setState(() {
            _items = updateItems(_items);
            print(_items);
          }),
        ),
        body: Row(
          children: [
            _listWithTitle(
              title: 'ImplicitlyAnimatedList',
              child: ImplicitlyAnimatedList(
                key: _resetKey,
                initialAnimation: _initialAnimation,
                insertDuration: Duration(milliseconds: 500),
                deleteDuration: Duration(milliseconds: 500),
                itemData: _items,
                itemBuilder: (context, item) => ListTile(title: Text('$item')),
                itemEquality: _customEquality ? _myCustomEquality : null,
              ),
            ),
            VerticalDivider(),
            _listWithTitle(
              title: 'SliverImplicitlyAnimatedList',
              child: CustomScrollView(
                slivers: [
                  SliverImplicitlyAnimatedList(
                    key: _resetKey,
                    initialAnimation: _initialAnimation,
                    insertDuration: Duration(milliseconds: 500),
                    deleteDuration: Duration(milliseconds: 500),
                    itemData: _items,
                    itemBuilder: (context, item) =>
                        ListTile(title: Text('$item')),
                    itemEquality: _customEquality ? _myCustomEquality : null,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSettingsButton() {
    return PopupMenuButton(
      icon: Icon(Icons.settings),
      itemBuilder: (context) => [
        CheckedPopupMenuItem(
          value: _SettingAction.toggleAnimation,
          checked: _initialAnimation,
          child: Text("Use initial animation?"),
        ),
        CheckedPopupMenuItem(
          value: _SettingAction.toggleEquality,
          checked: _customEquality,
          child: Text("Use custom equality?"),
        ),
        PopupMenuItem(
          value: _SettingAction.performReset,
          child: Text("Reset View"),
        ),
      ],
      onSelected: (value) => setState(() {
        switch (value) {
          case _SettingAction.toggleAnimation:
            _initialAnimation = !_initialAnimation;
            break;
          case _SettingAction.toggleEquality:
            _customEquality = !_customEquality;
            break;
          case _SettingAction.performReset:
            _resetKey = ValueKey(_resetKey.value + 1);
            _items = updateItems([]);
            break;
        }
      }),
    );
  }

  Widget _listWithTitle({required String title, required Widget child}) {
    return Expanded(
      child: Column(
        children: [
          Text(title, textAlign: TextAlign.center),
          Expanded(child: child),
        ],
      ),
    );
  }
}

enum _SettingAction { toggleAnimation, toggleEquality, performReset }

以上就是关于 implicitly_animated_list 插件的基本介绍和使用方法。希望这对你的项目有所帮助!


更多关于Flutter动画列表插件implicitly_animated_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动画列表插件implicitly_animated_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用implicitly_animated_list插件来创建动画列表的示例代码。这个插件允许你创建带有平滑动画效果的列表项。

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

dependencies:
  flutter:
    sdk: flutter
  implicitly_animated_list: ^0.7.0  # 请检查最新版本号

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

接下来,我们编写一个完整的Flutter应用示例,展示如何使用implicitly_animated_list

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Implicitly Animated List Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AnimatedListDemo(),
    );
  }
}

class AnimatedListDemo extends StatefulWidget {
  @override
  _AnimatedListDemoState createState() => _AnimatedListDemoState();
}

class _AnimatedListDemoState extends State<AnimatedListDemo> {
  final List<String> _items = List.generate(20, (i) => "Item ${i + 1}");
  final List<GlobalKey> _keys = List.generate(20, (i) => GlobalKey());

  void _addItem() {
    setState(() {
      _items.add("Item ${_items.length + 1}");
      _keys.add(GlobalKey());
    });
  }

  void _removeItem(int index) {
    setState(() {
      _items.removeAt(index);
      _keys.removeAt(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Implicitly Animated List Demo'),
        actions: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: _addItem,
          ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ImplicitlyAnimatedList<String>(
          items: _items,
          itemBuilder: (context, index, item) {
            return SlideTransition(
              position: Tween<Offset>(
                begin: Offset(0, 0.5),
                end: Offset.zero,
              ).animate(context.transition),
              child: FadeTransition(
                opacity: Tween<double>(begin: 0, end: 1).animate(context.transition),
                child: Card(
                  key: _keys[index],
                  child: ListTile(
                    title: Text(item),
                    trailing: IconButton(
                      icon: Icon(Icons.delete),
                      onPressed: () => _removeItem(index),
                    ),
                  ),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

在这个示例中:

  1. 我们创建了一个包含20个字符串项的列表_items,以及一个对应的全局键列表_keys,用于确保每个列表项具有唯一的键。
  2. ImplicitlyAnimatedList接受一个项列表items和一个项构建器itemBuilder
  3. itemBuilder函数为每个项创建了一个带有滑动和淡入动画效果的Card
  4. SlideTransitionFadeTransition用于创建滑动和淡入效果。
  5. 我们通过Tweenanimate方法将动画应用到每个列表项上。
  6. 提供了两个按钮,一个用于添加新项,另一个在每个列表项上用于删除该项。

这个示例展示了如何使用implicitly_animated_list来创建一个带有平滑动画效果的列表,同时展示了如何动态添加和删除列表项。

回到顶部