great_list_view 0.0.3 copy "great_list_view: ^0.0.3" to clipboard
great_list_view: ^0.0.3 copied to clipboard

outdated

A Flutter package that enhance the standard list view with implicit animations on changes and so on. See README.md file.

great_list_view #

A Flutter package that includes an animated list view. Just notify the list view of changes in your underlying list and the list view will automatically animate. You can also change the entire list and automatically dispatch the differences detected by the Myers alghoritm.

It works without necessarily specifying a List object, but simply using a builder callback. Also, this animated list view works well even with a very long list.

This package also provides a tree adapter to create a tree view without defining a new widget for it, but simply by converting your tree data into a linear list view, animated or not. Your tree data can be any data type, just describe it using a model with a set of callbacks.

Installing #

Add this to your pubspec.yaml file:

dependencies:
  great_list_view: ^0.0.1

and run;

flutter packages get

Example #

This is an example of how to use AnimatedSliverList with a ListAnimatedListDiffDispatcher, which works on List objects, to swap two lists with animations.

import 'package:flutter/material.dart';

import 'package:great_list_view/great_list_view.dart';
import 'package:worker_manager/worker_manager.dart';

void main() async {
  await Executor().warmUp();
  runApp(MyApp());
}

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

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Test App',
        theme: ThemeData(
          primarySwatch: Colors.yellow,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: SafeArea(
            child: Scaffold(
          body: AnimatedListExample(),
        )));
  }
}

class MyItem {
  final int id;
  final Color color;
  final double fixedHeight;
  const MyItem(this.id, [this.color = Colors.blue, this.fixedHeight]);
}

Widget buildItem(BuildContext context, MyItem item, final bool animating) {
  return GestureDetector(
      onTap: click,
      child: SizedBox(
          height: item.fixedHeight,
          child: DecoratedBox(
              key: !animating ? ValueKey(item) : null,
              decoration: BoxDecoration(
                  border: Border.all(color: Colors.black12, width: 0)),
              child: Container(
                  color: item.color,
                  margin: EdgeInsets.all(5),
                  padding: EdgeInsets.all(15),
                  child: Center(
                      child: Text(
                    "Item ${item.id}",
                    style: TextStyle(fontSize: 16),
                  ))))));
}

List<MyItem> listA = [
  MyItem(1, Colors.orange),
  MyItem(2),
  MyItem(3),
  MyItem(4),
  MyItem(5),
  MyItem(8, Colors.green)
];
List<MyItem> listB = [
  MyItem(2),
  MyItem(6),
  MyItem(5, Colors.pink, 100),
  MyItem(7),
  MyItem(8, Colors.green)
];

AnimatedListController controller = AnimatedListController();

final diff = ListAnimatedListDiffDispatcher<MyItem>(
  animatedListController: controller,
  currentList: listA,
  itemBuilder: buildItem,
  comparator: MyComparator.instance,
);

class MyComparator extends ListAnimatedListDiffComparator<MyItem> {
  MyComparator._();

  static MyComparator instance = MyComparator._();

  @override
  bool sameItem(MyItem a, MyItem b) => a.id == b.id;

  @override
  bool sameContent(MyItem a, MyItem b) =>
      a.color == b.color && a.fixedHeight == b.fixedHeight;
}

bool swapList = true;

void click() {
  if (swapList) {
    diff.dispatchNewList(listB);
  } else {
    diff.dispatchNewList(listA);
  }
  swapList = !swapList;
}

class AnimatedListExample extends StatefulWidget {
  @override
  _AnimatedListExampleState createState() => _AnimatedListExampleState();
}

class _AnimatedListExampleState extends State<AnimatedListExample> {
  @override
  Widget build(BuildContext context) {
    return Scrollbar(
        child: CustomScrollView(
      slivers: <Widget>[
        AnimatedSliverList(
          delegate: AnimatedSliverChildBuilderDelegate(
            (BuildContext context, int index, bool animating) {
              return buildItem(context, diff.currentList[index], animating);
            },
            childCount: () => diff.currentList.length,
          ),
          controller: controller,
        )
      ],
    ));
  }
}

Example #

This is an example of how to use TreeListAdapter with an AnimatedSliverList to create a editable tree view widget.

import 'dart:math';

import 'package:flutter/material.dart';

import 'package:worker_manager/worker_manager.dart';
import 'package:great_list_view/great_list_view.dart';

void main() async {
  await Executor().warmUp();
  init();
  runApp(MyApp());
}

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

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Test App',
        theme: ThemeData(
          primarySwatch: Colors.yellow,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: SafeArea(
            child: Scaffold(
          body: TreeExample(),
        )));
  }
}

const List<String> kNames = [
  "Liam",
  "Olivia",
  "Noah",
  "Emma",
  "Oliver",
  "Ava",
  "William",
  "Sophia",
  "Elijah",
  "Isabella",
  "James",
  "Charlotte",
  "Benjamin",
  "Amelia",
  "Lucas",
  "Mia",
  "Mason",
  "Harper",
  "Ethan",
  "Evelyn"
];

Random r = Random();

class MyNode {
  final String text;
  List<MyNode> children = [];
  MyNode parent;

  MyNode(this.text);

  void add(MyNode n, [int index]) {
    (index == null) ? children.add(n) : children.insert(index, n);
    n.parent = this;
  }

  @override
  String toString() => text;
}

MyNode root = MyNode("Me");

void init() {
  buildTree(r, root, 6);
}

void buildTree(Random r, MyNode node, int maxChildren) {
  int n = maxChildren > 0 ? r.nextInt(maxChildren) : 0;
  if (n == 0) return;
  for (int i = 0; i < n; i++) {
    MyNode child = MyNode(kNames[r.nextInt(kNames.length)]);
    buildTree(r, child, maxChildren - 1);
    node.add(child);
  }
}

Set<MyNode> collapsedMap = Set();

AnimatedListController controller = AnimatedListController();
ScrollController scrollController = ScrollController();

Widget buildIconButton(Color color, Icon icon, void Function() onPressed) {
  return DecoratedBox(
      decoration: BoxDecoration(border: Border.all(color: color, width: 2)),
      child: SizedBox(
          width: 20,
          height: 25,
          child: IconButton(
              padding: new EdgeInsets.all(0.0),
              iconSize: 15,
              icon: icon,
              onPressed: onPressed)));
}

Widget buildItem(TreeListAdapter<MyNode> adapter, BuildContext context,
    int index, bool animating) {
  final MyNode node = adapter.indexToNode(index);
  int level = adapter.levelOf(node);
  String spaces = "";
  for (int i = 0; i < level; i++) spaces += "     ";
  return ListTile(
    key: animating ? ValueKey(node) : null,
    dense: true,
    trailing: adapter.isLeaf(node)
        ? null
        : ArrowButton(
            expanded: adapter.isNodeExpanded(node),
            turns: 0.25,
            icon: const Icon(Icons.keyboard_arrow_right),
            duration: const Duration(milliseconds: 500),
            onTap: (expanded) {
              if (expanded) {
                adapter.notifyNodeExpanding(node, () {
                  collapsedMap.remove(node);
                }, index: index, controller: controller);
              } else {
                adapter.notifyNodeCollapsing(node, () {
                  collapsedMap.add(node);
                }, index: index, controller: controller, builder: buildItem);
              }
              controller.dispatchChanges();
            },
          ),
    leading: SizedBox(
        width: 40,
        child: Row(
          children: [
            buildIconButton(Colors.red, const Icon(Icons.remove), () {
              adapter.notifyNodeRemoving(node, () {
                node.parent.children.remove(node);
              }, index: index, controller: controller, builder: buildItem);
              controller.dispatchChanges();
            }),
            buildIconButton(Colors.green, const Icon(Icons.add), () {
              var newTree = MyNode(kNames[r.nextInt(kNames.length)]);
              adapter.notifyNodeInserting(newTree, node, 0, () {
                node.add(newTree, 0);
              }, index: index, controller: controller);
              controller.dispatchChanges();
            }),
          ],
        )),
    title: Text("$spaces$node", style: TextStyle(fontSize: 14)),
  );
}

TreeListAdapter<MyNode> adapter = TreeListAdapter<MyNode>(
  childAt: (node, index) => node.children[index],
  childrenCount: (node) => node.children.length,
  parentOf: (node) => node.parent,
  indexOfChild: (parent, node) => parent.children.indexOf(node),
  isNodeExpanded: (node) => !collapsedMap.contains(node),
  includeRoot: true,
  root: root,
);

class TreeExample extends StatefulWidget {
  @override
  _TreeExampleState createState() => _TreeExampleState();
}

class _TreeExampleState extends State<TreeExample> {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(controller: scrollController, slivers: <Widget>[
      AnimatedSliverList(
          controller: controller,
          delegate: AnimatedSliverChildBuilderDelegate(
            (BuildContext context, int index, bool animating) {
              return buildItem(adapter, context, index, animating);
            },
            childCount: () => adapter.count,
          )),
    ]);
  }
}
95
likes
0
pub points
89%
popularity

Publisher

unverified uploader

A Flutter package that enhance the standard list view with implicit animations on changes and so on. See README.md file.

Homepage

License

unknown (license)

Dependencies

diffutil_dart, flutter, worker_manager

More

Packages that depend on great_list_view