简体   繁体   中英

flutter how to make Hero like animation between widgets on the same page

I have a button with a text, and when I pressed the button, a text widget with the same text is added to the same page. I'd like to add Hero like animation between them. I guess what I need is SlideTransition, but I don't know how to slide from one widget position to another widget position. Is it possible to do? What widget (or class) should I look into?

Here's the code I want to do (but doesn't work since Hero doesn't work on the same page widgets):

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> text = [];
  String buttonTag = "0";


  @override
  Widget build(BuildContext context) {
    List<Widget> textWidgets = [];
    for (int i = 0; i < text.length; ++i) {
      textWidgets.add(
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Hero(tag: "${i}", child: Text(text[i])),
        )
      );
    }

    return SafeArea(
      child: Scaffold(
        body: Center(
          child: Column(
            children: <Widget>[
                  RaisedButton(
                    child: Hero(
                        tag: buttonTag,
                        child: Text("abcde${text.length}")),
                    onPressed: () {
                      setState(() {
                        text.add("abcde${text.length}");
                        buttonTag = "${text.length}";
                      });
                    },
                  )
                ] + textWidgets,
          ),
        ),
      ),
    );
  }
}

Not exactly the answer to the question but instead of Hero (if it's possible) you can use AnimatedList to get the same result.

Code snippet

import 'package:flutter/material.dart';
import 'dart:math';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
  final List<Text> _textWidgets = [];
  var rng = new Random();
  _addItem() {
    setState(() {
      _listKey.currentState.insertItem(_textWidgets.length,
          duration: const Duration(milliseconds: 500));
      int id = rng.nextInt(100);
      _textWidgets.add(Text('item $id'));
    });
  }

  Widget _buildItem(
      BuildContext context, Text item, Animation<double> animation) {
    final offsetAnimation = Tween<Offset>(
      begin: Offset(1.0, 0.0),
      end: Offset(0.0, 0.0),
    ).animate(animation);
    return SlideTransition(
      position: offsetAnimation,
      child: SizedBox(
        height: 50.0,
        child: Center(
          child: item,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("Add Item"),
              onPressed: () {
                setState(() {
                  _addItem();
                });
              },
            ),
            Expanded(
              child: AnimatedList(
                key: _listKey,
                initialItemCount: _textWidgets.length,
                itemBuilder: (context, index, animation) {
                  return _buildItem(context, _textWidgets[index], animation);
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

I think it's possible by this llibrary which named: LocalHero

在此处输入图像描述

Implement your self

The AnimationController , Tween , AnimatedBuilder are key components.

This is a sample and code about this.

在此处输入图像描述

Don't use AnimatedController.animate in AnimatedBuilder builder.

evaluate is enough. Because the builder function is called every ticker frame.

Use AnimationController.animate as class member field.

class _AuthorizedState extends State<_Authorized> with SingleTickerProviderStateMixin {
  late final _menuAC = AnimationController(vsync: this, duration: 200.ms);
  late final isFilterOpen = ValueNotifier(false)..addListener(_handleFilterOpenChanged);

  late final filterColor =
      ColorTween(begin: context.color.primaryContainer, end: context.color.secondaryContainer)
          .animate(_menuAC);
  late final filterBorderRadius = Tween<double>(begin: 12, end: 0).animate(_menuAC);

  void _handleFilterOpenChanged() {
    print(isFilterOpen.value);
    if (isFilterOpen.value) {
      _menuAC.forward();
    } else {
      _menuAC.reverse();
    }
  }

  @override
  void dispose() {
    _menuAC.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AppScaffold(
      child: LayoutBuilder(
        builder: (context, cons) => Stack(
          children: [
                        AnimatedBuilder(
              animation: _menuAC,
              builder: (context, child) {
                return Positioned(
                  bottom: Tween(begin: 16.0, end: 0.0).evaluate(_menuAC),
                  right: Tween(begin: 16.0, end: 0.0).evaluate(_menuAC),
                  child: ClipRRect(
                    borderRadius: BorderRadius.only(
                      topLeft: const Radius.circular(12.0),
                      topRight: const Radius.circular(12.0),
                      bottomLeft:
                          Radius.circular(Tween<double>(begin: 12, end: 0).evaluate(_menuAC)),
                      bottomRight:
                          Radius.circular(Tween<double>(begin: 12, end: 0).evaluate(_menuAC)),
                    ),
                    child: Container(
                      width: Tween(begin: 56.0, end: cons.maxWidth).evaluate(_menuAC),
                      height: Tween(begin: 56.0, end: min(cons.maxHeight * 0.7, 500.0))
                          .evaluate(_menuAC),
                      decoration: BoxDecoration(
                        color: filterColor.value,
                      ),
                      child: child,
                    ),
                  ),
                );
              },
              child: Material(
                elevation: 24,
                color: Colors.transparent,
                child: InkWell(
                  onTap: () {
                    isFilterOpen.value = !isFilterOpen.value;
                  },
                  child: const Center(child: Icon(MdiIcons.filter)),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM