declarative_animated_list 0.2.0 copy "declarative_animated_list: ^0.2.0" to clipboard
declarative_animated_list: ^0.2.0 copied to clipboard

An implementation of animated list widget that will be automatically updated based on different lists snippets.

example/lib/main.dart

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @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({required this.bloc, super.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, snapshot);
        },
      ),
      floatingActionButton: _buildFab(context),
    );
  }

  Widget _buildBody(
    BuildContext context,
    AsyncSnapshot<ToDosState> snapshot,
  ) {
    if (snapshot.hasData) {
      return _buildBasedOnState(context, snapshot.data!);
    } else if (snapshot.hasError) {
      return Center(
        child: Text(
          'Error occurred: ${snapshot.error}',
          textAlign: TextAlign.center,
          style: const TextStyle(color: Colors.black),
        ),
      );
    } else {
      return const Center(child: CircularProgressIndicator());
    }
  }

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

  Widget _buildFadeAndSizeTransitioningTile(
    Animation<double> animation,
    ToDoPresentationModel model,
  ) {
    return FadeTransition(
      opacity: animation,
      child: SizeTransition(sizeFactor: animation, child: _buildTile(model)),
    );
  }

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

  Widget _buildFab(BuildContext context) {
    return FloatingActionButton(
      child: const Icon(Icons.add),
      onPressed: () => showDialog<void>(
        context: context,
        builder: (ctx) {
          final controller = TextEditingController();
          final size = MediaQuery.sizeOf(ctx);
          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: const Icon(Icons.check),
                    onPressed: () {
                      bloc.addToDo.add(AddToDoEvent(controller.text));
                      Navigator.of(ctx).pop();
                    },
                  )
                ],
              ),
            ),
          );
        },
      ),
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty('bloc', bloc));
  }
}
21
likes
140
points
821
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

An implementation of animated list widget that will be automatically updated based on different lists snippets.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on declarative_animated_list