declarative_animated_list 0.0.2

Declarative animated list #

An implementation of animated list widget that will be automatically updated based on different lists snippets. Based on Android's DiffUtil with slight changes to support Flutter's declarative UI.


//Create a list tile, wrapped with an animation applying widget
Widget _buildAnimatedTile(final Animation<double> animation, final PresentationModel model) {
  return FadeTransition(
    opacity: animation,
    child: SizeTransition(
      sizeFactor: animation,
      axisAlignment: 0.0,
      child: SomeWidget(model),
    ),
  );
}

Widget _buildRemovingTile(final Animation<double> animation, final PresentationModel model) { 
  //... 
}

final DeclarativeList<PresentationModel> declarativeList = DeclarativeList(
  items: presentationModels,
  itemBuilder: (BuildContext ctx, PresentationModel model, int index, Animation<double> animation) {
    return _buildAnimatedTile(animation, model);
  },
  removeBuilder: (BuildContext ctx, PresentationModel model, int index, Animation<double> animation) {
    return _buildRemovingTile(animation, model);
  }  
);


And... that's it!

Getting Started #

1. Add dependency to your pubspec.yaml

dependencies:
  declarative_animated_list: ^0.0.1

2. Import it #

import 'package:declarative_animated_list/declarative_animated_list.dart';

3. Use it! Refer to the examples folder if needed.

[0.0.1] - 14.06.2019.

  • Initial release.

[0.0.2] - 15.06.2019.

  • Apply README changes in order to make description shorter.

example/lib/main.dart

import 'package:declarative_animated_list/declarative_animated_list.dart';
import 'package:example/bloc.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final ToDosBloc bloc = ToDosBloc();
    return MaterialApp(
      title: 'Declarative Animated List Demo',
      theme: ThemeData.dark(),
      home: ToDosPage(
        bloc: bloc,
      ),
    );
  }
}

class ToDosPage extends StatelessWidget {
  final ToDosBloc bloc;

  const ToDosPage({Key key, @required this.bloc}) : super(key: key);

  @override
  Widget build(final BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: StreamBuilder<ToDosState>(
          stream: bloc.toDosState,
          builder: (_, final AsyncSnapshot<ToDosState> snapshot) {
            return _buildBody(context, bloc, snapshot);
          }),
      floatingActionButton: _buildFab(context),
    );
  }

  Widget _buildBody(final BuildContext context, final ToDosBloc bloc,
      final AsyncSnapshot<ToDosState> snapshot) {
    if (snapshot.hasData) {
      return _buildBasedOnState(context, snapshot, bloc);
    } else if (snapshot.hasError) {
      return Center(
          child: Text(
        "Error occurred: ${snapshot.error}",
        textAlign: TextAlign.center,
        style:
            Theme.of(context).textTheme.display1.copyWith(color: Colors.black),
      ));
    } else {
      return Center(
        child: CircularProgressIndicator(),
      );
    }
  }

  Widget _buildBasedOnState(final BuildContext context,
      final AsyncSnapshot<ToDosState> snapshot, final ToDosBloc bloc) {
    final ToDosState viewModel = snapshot.data;
    final List<ToDoPresentationModel> toDos = viewModel.toDos.toList()
      ..sort((left, right) {
        if (left.completed == right.completed) {
          return left.description.compareTo(right.description);
        } else {
          return left.completed ? 1 : -1;
        }
      });
    if (toDos.isEmpty) {
      return Center(
          child: Container(
        child: Icon(
          Icons.delete_outline,
          size: MediaQuery.of(context).size.height * 0.4,
          color: Theme.of(context).accentColor,
        ),
      ));
    } else {
      return DeclarativeList(
        items: toDos,
        insertDuration: const Duration(milliseconds: 500),
        removeDuration: const Duration(milliseconds: 500),
        itemBuilder:
            (_, ToDoPresentationModel model, __, Animation<double> anim) =>
                _buildFadeAndSizeTransitioningTile(anim, model, bloc),
        removeBuilder:
            (_, ToDoPresentationModel model, __, Animation<double> anim) =>
                _buildFadeAndSizeTransitioningTile(anim, model, bloc),
      );
    }
  }

  Widget _buildFadeAndSizeTransitioningTile(final Animation<double> anim,
      final ToDoPresentationModel model, final ToDosBloc bloc) {
    return FadeTransition(
      opacity: anim,
      child: SizeTransition(
        sizeFactor: anim,
        axisAlignment: 0.0,
        child: _buildTile(model, bloc),
      ),
    );
  }

  Widget _buildTile(final ToDoPresentationModel toDo, final ToDosBloc bloc) {
    return ListTile(
      title: Text(toDo.description),
      leading: IconButton(
        icon: Icon(
          toDo.completed ? Icons.check : Icons.sync,
        ),
        onPressed: () => bloc.changeToDoStatus
            .add(ChangeCompletionStatusEvent(toDo, !toDo.completed)),
      ),
      onLongPress: () => bloc.removeToDo.add(RemoveToDoEvent(toDo)),
    );
  }

  Widget _buildFab(final BuildContext context) {
    return FloatingActionButton(
      child: Icon(Icons.add),
      onPressed: () => showDialog(
          context: context,
          builder: (final BuildContext ctx) {
            final TextEditingController controller = TextEditingController();
            final Size size = MediaQuery.of(ctx).size;
            return Container(
              height: size.height * 0.5,
              width: size.width * 0.5,
              padding: const EdgeInsets.all(16.0),
              child: Material(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: TextField(controller: controller),
                    ),
                    IconButton(
                      icon: Icon(Icons.check),
                      onPressed: () {
                        bloc.addToDo.add(AddToDoEvent(controller.text));
                        Navigator.of(ctx).pop();
                      },
                    )
                  ],
                ),
              ),
            );
          }),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  declarative_animated_list: ^0.0.2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:declarative_animated_list/declarative_animated_list.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
12
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
54
Learn more about scoring.

We analyzed this package on Jul 15, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.4.0
  • pana: 0.12.19
  • Flutter: 1.7.8+hotfix.3

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Maintenance suggestions

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11
meta 1.1.6 1.1.7
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test
mockito 4.1.0

Admin