Animated Stream List In Flutter

Animated Stram List In Flutter
Animated Stram List In Flutter

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