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

AnimateTo animates its child to a new position requiring no calculation.

example/lib/main.dart

import 'dart:async';

import 'package:animated_to/animated_to.dart';
import 'package:example/listview_page.dart';
import 'package:example/scrollable_page.dart';
import 'package:example/simple_demo_page.dart';
import 'package:example/todo_cards_page.dart';
import 'package:example/two_lines_page.dart';
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: const AnimatedToSamplePage());
  }
}

class AnimatedToSamplePage extends StatefulWidget {
  const AnimatedToSamplePage({super.key});

  @override
  State<AnimatedToSamplePage> createState() => _AnimatedToSamplePageState();
}

class _AnimatedToSamplePageState extends State<AnimatedToSamplePage>
    with TickerProviderStateMixin {
  var _isExpanded = true;

  /// Some item objects. In this demo, simply a list of [String].
  final _items = List.generate(
    50,
    (index) => index.toString(),
  );

  final _scrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    final children = _items
        .map(
          (item) => _Card(
            item: item,
            index: _items.indexOf(item),
            opacity: 1.0,
            isExpanded: _isExpanded,
            vsync: this,
            enabled: true,
            controller: _scrollController,
          ),
        )
        .toList();

    return Scaffold(
      drawer: Drawer(
        backgroundColor: Colors.grey[300],
        child: SafeArea(
          child: Column(
            spacing: 4,
            children: [
              _DrawerMenuItem(
                title: 'TODO cards',
                vsync: this,
                onTap: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => const TodoCardsPage(),
                    ),
                  );
                },
              ),
              _DrawerMenuItem(
                title: 'Two line boxes',
                vsync: this,
                onTap: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => const TwoLinesPage(),
                    ),
                  );
                },
              ),
              _DrawerMenuItem(
                title: 'Scrollable',
                vsync: this,
                onTap: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => const ScrollablePage(),
                    ),
                  );
                },
              ),
              _DrawerMenuItem(
                title: 'Simple Demo',
                vsync: this,
                onTap: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => const SimpleDemoPage(),
                    ),
                  );
                },
              ),
              _DrawerMenuItem(
                title: 'List menu page',
                vsync: this,
                onTap: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => const ListPage(),
                    ),
                  );
                },
              ),
            ],
          ),
        ),
      ),
      appBar: AppBar(title: const Text('Animated Sample Page')),
      // TODO(chooyan-eng): note that [AnimatedTo] doesn't work on scrollable widgets
      body: SingleChildScrollView(
        controller: _scrollController,
        child: Center(
          child: Padding(
            padding: const EdgeInsets.all(4),
            child: switch (_isExpanded) {
              // toggle [Wrap] and [Column] with animation
              true => Wrap(
                  spacing: 4,
                  runSpacing: 4,
                  children: children,
                ),
              false => Column(
                  mainAxisSize: MainAxisSize.min,
                  children: children,
                ),
            },
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          setState(() => _isExpanded = !_isExpanded);
        },
        icon: Icon(_isExpanded ? Icons.collections : Icons.expand),
        label: Text(_isExpanded ? 'Collapse' : 'Expand'),
      ),
    );
  }
}

/// A circle or a rectangle shaped widget to be animated with [AnimatedTo].
class _Card extends StatelessWidget {
  const _Card({
    required this.item,
    required this.index,
    required this.opacity,
    required this.isExpanded,
    required this.vsync,
    required this.enabled,
    required this.controller,
  });

  final String item;
  final int index;
  final double opacity;
  final bool isExpanded;
  final TickerProvider vsync;
  final bool enabled;
  final ScrollController? controller;
  @override
  Widget build(BuildContext context) {
    final size = 60.0;
    return AnimatedTo(
      // try either of [appearingFrom] or [slidingFrom]
      appearingFrom: const Offset(100, 0),
      // slidingFrom: const Offset(0, 100),

      // [GlobalObjectKey] is required to identify the widget
      globalKey: GlobalObjectKey(item),
      duration: Duration(milliseconds: 300 + (10 * index)),
      curve: Curves.easeOutQuad,
      enabled: enabled,
      controller: controller,
      onEnd: (cause) {
        switch (cause) {
          case AnimationEndCause.interrupted:
            // called when the animation is interrupted by another animation
            break;
          case AnimationEndCause.completed:
            // called when the animation is completed
            break;
        }
      },
      // [AnimatedTo] can be combined with some Animated widgets
      child: Container(
        // duration: Duration(milliseconds: 300 + (10 * index)),
        // curve: Curves.easeIn,
        decoration: BoxDecoration(
          color: Colors.primaries[int.parse(item) % Colors.primaries.length]
              .withAlpha(isExpanded ? 255 : 128),
          borderRadius: isExpanded
              ? BorderRadius.circular(100)
              : BorderRadius.circular(4),
        ),
        margin: const EdgeInsets.all(2),
        width: size,
        height: size,
        child: Center(
          child: Text(
            item,
            style: TextStyle(
              color: Colors.white.withAlpha(isExpanded ? 255 : 128),
              fontSize: 24,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }
}

class _DrawerMenuItem extends StatefulWidget {
  const _DrawerMenuItem({
    required this.title,
    required this.onTap,
    required this.vsync,
  });

  final String title;
  final VoidCallback onTap;
  final TickerProvider vsync;

  @override
  State<_DrawerMenuItem> createState() => _DrawerMenuItemState();
}

class _DrawerMenuItemState extends State<_DrawerMenuItem> {
  var _preparing = true;
  @override
  void initState() {
    super.initState();

    Timer(const Duration(milliseconds: 200), () {
      setState(() => _preparing = false);
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_preparing) {
      return const SizedBox.shrink();
    }
    return AnimatedTo(
      globalKey: GlobalObjectKey(widget.title),
      appearingFrom: const Offset(0, -100),
      duration: const Duration(milliseconds: 500),
      curve: Curves.easeOutQuad,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
        child: Material(
          borderRadius: BorderRadius.circular(12),
          color: Theme.of(context).colorScheme.surface,
          child: InkWell(
            borderRadius: BorderRadius.circular(12),
            onTap: widget.onTap,
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
              child: Row(
                children: [
                  Text(
                    widget.title,
                    style: Theme.of(context).textTheme.titleMedium?.copyWith(
                          fontWeight: FontWeight.w500,
                        ),
                  ),
                  const Spacer(),
                  Icon(
                    Icons.arrow_forward_ios,
                    size: 16,
                    color: Theme.of(context).colorScheme.onSurfaceVariant,
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}
81
likes
160
points
516
downloads

Publisher

unverified uploader

Weekly Downloads

AnimateTo animates its child to a new position requiring no calculation.

Repository (GitHub)
View/report issues

Topics

#animation

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on animated_to