


animated_stream_list.dart
export 'package:fluttertutorial/animated_list/animated_stream_list.dart';
home_page.dart
home_page.dart
Main.dart
import 'package:flutter/material.dart'; import 'home_page.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Animated Stream List', home: HomePage(), ); } }
todo.dart
class Todo implements Comparable<Todo> { final String title; final String content; DateTime _changedAt; bool _done; Todo({this.title, this.content}) : _changedAt = DateTime.now(), _done = false; DateTime get changedAt => _changedAt; bool get done => _done; set done(bool done) { _done = done; _changedAt = DateTime.now(); } @override bool operator ==(other) => other is Todo && other._changedAt == _changedAt; @override int get hashCode => _changedAt.hashCode; @override int compareTo(Todo other) => this._changedAt.millisecondsSinceEpoch - other._changedAt.millisecondsSinceEpoch; }
todos_bloc.dart
import 'dart:async'; import 'todo.dart'; class TodoBloc { final List<Todo> _todoList = []; final StreamController<List<Todo>> _todoListStream = StreamController(); Stream<List<Todo>> get todoListStream => _todoListStream.stream; TodoBloc() { _todoListStream.add(_todoList); } void addTodo(Todo todo) { _todoList.add(todo); _todoList.sort(); _todoListStream.add(_todoList); } void removeTodo(int index) { _todoList.removeAt(index); _todoListStream.add(_todoList); } void toggleDone(int index) { final todo = _todoList[index]; todo.done = !todo.done; _todoListStream.add(_todoList); } void dispose() { _todoListStream.close(); } }
animated_stream_list.dart
import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:fluttertutorial/animated_list/diff_applier.dart'; import 'package:fluttertutorial/animated_list/list_controller.dart'; import 'package:fluttertutorial/animated_list/myers_diff.dart'; class AnimatedStreamList<E> extends StatefulWidget { final Stream<List<E>> streamList; final List<E> initialList; final AnimatedStreamListItemBuilder<E> itemBuilder; final AnimatedStreamListItemBuilder<E> itemRemovedBuilder; final Axis scrollDirection; final bool reverse; final ScrollController scrollController; final bool primary; final ScrollPhysics scrollPhysics; final bool shrinkWrap; final EdgeInsetsGeometry padding; final Equalizer equals; AnimatedStreamList({ @required this.streamList, this.initialList, @required this.itemBuilder, @required this.itemRemovedBuilder, this.scrollDirection: Axis.vertical, this.reverse: false, this.scrollController, this.primary, this.scrollPhysics, this.shrinkWrap: false, this.padding, this.equals, }); @override State<StatefulWidget> createState() => _AnimatedStreamListState<E>(); } class _AnimatedStreamListState<E> extends State<AnimatedStreamList<E>> with WidgetsBindingObserver { final GlobalKey<AnimatedListState> _globalKey = GlobalKey(); ListController<E> _listController; DiffApplier<E> _diffApplier; DiffUtil<E> _diffUtil; StreamSubscription<List<E>> _subscription; void startListening() { _subscription?.cancel(); _subscription = widget.streamList.listen((list) async { final diffList = await _diffUtil.calculateDiff(_listController.items, list, equalizer: widget.equals); _diffApplier.applyDiffs(diffList); }); } void stopListening() { _subscription?.cancel(); _subscription = null; } @override void initState() { super.initState(); _listController = ListController( key: _globalKey, items: widget.initialList ?? <E>[], itemRemovedBuilder: widget.itemRemovedBuilder, ); _diffApplier = DiffApplier(_listController); _diffUtil = DiffUtil(); startListening(); } @override void dispose() { stopListening(); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.resumed: startListening(); break; case AppLifecycleState.paused: stopListening(); break; default: break; } } @override Widget build(BuildContext context) { return AnimatedList( initialItemCount: _listController.items.length, key: _globalKey, scrollDirection: widget.scrollDirection, reverse: widget.reverse, primary: widget.primary, controller: widget.scrollController, physics: widget.scrollPhysics, padding: widget.padding, shrinkWrap: widget.shrinkWrap, itemBuilder: (BuildContext context, int index, Animation<double> animation) => widget.itemBuilder( _listController[index], index, context, animation, ), ); } }
diff_applier.dart
import 'package:fluttertutorial/animated_list/diff_payload.dart'; import 'package:fluttertutorial/animated_list/list_controller.dart'; class DiffApplier<E> { final ListController<E> _controller; final DiffVisitor _visitor; DiffApplier(this._controller) : _visitor = _Visitor(_controller); void applyDiffs(List<Diff> diffs) { diffs.forEach((diff) => diff.accept(_visitor)); } } class _Visitor<E> implements DiffVisitor { final ListController<E> _controller; const _Visitor(this._controller); @override void visitChangeDiff(ChangeDiff diff) { if (diff.size > diff.items.length) { int sizeDifference = diff.size - diff.items.length; while (sizeDifference > 0) { _controller.removeItemAt(diff.index + sizeDifference); sizeDifference--; } } else if (diff.items.length > diff.size) { int insertIndex = diff.size; while (insertIndex < diff.items.length) { _controller.insert(insertIndex + diff.index, diff.items[insertIndex]); insertIndex++; } } final changedItems = diff.items.take(diff.size).toList(); _controller.listChanged(diff.index, changedItems); } @override void visitDeleteDiff(DeleteDiff diff) { for (int i = 0; i < diff.size; i++) { _controller.removeItemAt(diff.index); } } @override void visitInsertDiff(InsertDiff diff) { for (int i = 0; i < diff.size; i++) { _controller.insert(diff.index + i, diff.items[i]); } } }
diff_payload.dart
abstract class DiffVisitor { void visitInsertDiff(InsertDiff diff); void visitDeleteDiff(DeleteDiff diff); void visitChangeDiff(ChangeDiff diff); } abstract class Diff implements Comparable<Diff> { final int index; final int size; const Diff(this.index, this.size); @override String toString() { return "${this.runtimeType.toString()} index: $index, size: $size"; } @override int compareTo(Diff other) => index - other.index; void accept(DiffVisitor visitor); } class InsertDiff<E> extends Diff { final List<E> items; InsertDiff(int index, int size, this.items) : super(index, size); @override void accept(DiffVisitor visitor) => visitor.visitInsertDiff(this); } class DeleteDiff extends Diff { DeleteDiff(int index, int size) : super(index, size); @override void accept(DiffVisitor visitor) => visitor.visitDeleteDiff(this); } class ChangeDiff<E> extends Diff { final List<E> items; ChangeDiff(int index, int size, this.items) : super(index, size); @override void accept(DiffVisitor visitor) => visitor.visitChangeDiff(this); }
list_controller.dart
import 'package:flutter/material.dart'; typedef Widget AnimatedStreamListItemBuilder<T>( T item, int index, BuildContext context, Animation<double> animation, ); class ListController<E> { final GlobalKey<AnimatedListState> key; final List<E> items; final Duration duration; final AnimatedStreamListItemBuilder<E> itemRemovedBuilder; ListController({ @required this.key, @required this.items, @required this.itemRemovedBuilder, this.duration = const Duration(milliseconds: 300), }) : assert(key != null), assert(itemRemovedBuilder != null), assert(items != null); AnimatedListState get _list => key.currentState; void insert(int index, E item) { items.insert(index, item); _list.insertItem(index, duration: duration); } void removeItemAt(int index) { E item = items.removeAt(index); _list.removeItem( index, (BuildContext context, Animation<double> animation) => itemRemovedBuilder(item, index, context, animation), duration: duration, ); } void listChanged(int startIndex, List<E> itemsChanged) { int i = 0; for (E item in itemsChanged) { items[startIndex + i] = item; i++; } // ignore: invalid_use_of_protected_member _list.setState(() {}); } int get length => items.length; E operator [](int index) => items[index]; int indexOf(E item) => items.indexOf(item); }
myers_diff.dart
import 'package:flutter/foundation.dart'; import 'package:fluttertutorial/animated_list/diff_payload.dart'; import 'package:fluttertutorial/animated_list/path_node.dart'; typedef bool Equalizer(dynamic item1, dynamic item2); class DiffUtil<E> { static Equalizer eq; Future<List<Diff>> calculateDiff(List<E> oldList, List<E> newList, {Equalizer equalizer}) { eq = equalizer; final args = _DiffArguments<E>(oldList, newList); return compute(_myersDiff, args); } } class _DiffArguments<E> { final List<E> oldList; final List<E> newList; _DiffArguments(this.oldList, this.newList); } List<Diff> _myersDiff<E>(_DiffArguments<E> args) { final List<E> oldList = args.oldList; final List<E> newList = args.newList; if (oldList == null) throw ArgumentError("oldList is null"); if (newList == null) throw ArgumentError("newList is null"); if (oldList == newList) return []; final oldSize = oldList.length; final newSize = newList.length; if (oldSize == 0) { return [InsertDiff(0, newSize, newList)]; } if (newSize == 0) { return [DeleteDiff(0, oldSize)]; } final equals = DiffUtil.eq != null ? DiffUtil.eq : (a, b) => a == b; final path = _buildPath(oldList, newList, equals); final diffs = _buildPatch(path, oldList, newList)..sort(); return diffs.reversed.toList(growable: true); } PathNode _buildPath<E>(List<E> oldList, List<E> newList, Equalizer equals) { final oldSize = oldList.length; final newSize = newList.length; final int max = oldSize + newSize + 1; final size = (2 * max) + 1; final int middle = size ~/ 2; final List<PathNode> diagonal = List(size); diagonal[middle + 1] = Snake(0, -1, null); for (int d = 0; d < max; d++) { for (int k = -d; k <= d; k += 2) { final int kmiddle = middle + k; final int kplus = kmiddle + 1; final int kminus = kmiddle - 1; PathNode prev; int i; if ((k == -d) || (k != d && diagonal[kminus].originIndex < diagonal[kplus].originIndex)) { i = diagonal[kplus].originIndex; prev = diagonal[kplus]; } else { i = diagonal[kminus].originIndex + 1; prev = diagonal[kminus]; } diagonal[kminus] = null; int j = i - k; PathNode node = DiffNode(i, j, prev); while (i < oldSize && j < newSize && equals(oldList[i], newList[j])) { i++; j++; } if (i > node.originIndex) { node = Snake(i, j, node); } diagonal[kmiddle] = node; if (i >= oldSize && j >= newSize) { return diagonal[kmiddle]; } } diagonal[middle + d - 1] = null; } throw Exception(); } List<Diff> _buildPatch<E>(PathNode path, List<E> oldList, List<E> newList) { if (path == null) throw ArgumentError("path is null"); final List<Diff> diffs = List(); if (path.isSnake()) { path = path.previousNode; } while (path != null && path.previousNode != null && path.previousNode.revisedIndex >= 0) { if (path.isSnake()) throw Exception(); int i = path.originIndex; int j = path.revisedIndex; path = path.previousNode; int iAnchor = path.originIndex; int jAnchor = path.revisedIndex; List<E> original = oldList.sublist(iAnchor, i); List<E> revised = newList.sublist(jAnchor, j); if (original.length == 0 && revised.length != 0) { diffs.add(InsertDiff(iAnchor, revised.length, revised)); } else if (original.length > 0 && revised.length == 0) { diffs.add(DeleteDiff(iAnchor, original.length)); } else { diffs.add(ChangeDiff(iAnchor, original.length, revised)); } if (path.isSnake()) { path = path.previousNode; } } return diffs; }
path_node.dart
abstract class PathNode { final int originIndex; final int revisedIndex; final PathNode previousNode; PathNode(this.originIndex, this.revisedIndex, this.previousNode); bool isSnake(); bool isBootStrap() => originIndex < 0 || revisedIndex < 0; PathNode previousSnake() { if (isBootStrap()) return null; if (!isSnake() && previousNode != null) return previousNode.previousSnake(); return this; } @override String toString() { final buffer = StringBuffer(); buffer.write("["); PathNode node = this; while (node != null) { buffer.write("("); buffer.write("${node.originIndex.toString()}"); buffer.write(","); buffer.write("${node.revisedIndex.toString()}"); buffer.write(")"); node = node.previousNode; } buffer.write("]"); return buffer.toString(); } } class Snake extends PathNode { Snake(int originIndex, int revisedIndex, PathNode previousNode) : super(originIndex, revisedIndex, previousNode); @override bool isSnake() => true; } class DiffNode extends PathNode { DiffNode(int originIndex, int revisedIndex, PathNode previousNode) : super(originIndex, revisedIndex, previousNode == null ? null : previousNode.previousSnake()); @override bool isSnake() => false; }
The flutter tutorial is a website that bring you the latest and amazing resources of code. All the languages codes are included in this website. The languages like flutter, android, java,kotlin etc.with the help of this languages any user can develop the beautiful application
For more information about Flutter. visit www.fluttertutorial.in