“This is the 14th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

preface

Introduced a few Hero animation, we come to a Hero animation application case. In some applications, the elements of the list are the same as the content of the details, so switching to the details with the Hero animation feels seamless and the user experience is better. For example, we want to implement the following:

Train of thought

The effect above is that the list and details share the avatar and avatar background color. The combination of the two is a Stack component, so it can be done using the Hero component. Then there is the movement of Hero component. We made horizontal movement first and then vertical movement, so that the transition experience will be better. This can be completed with our customized RectTween. Here is how we implemented each part.

List elements

List element We define a HeroListItem class. The entire list element needs to be clicked into details, wrapped with GestureDetector. The landscape layout is then done using the Row component, and the Stack component is used for the portrait part. HeroListItem builds as follows:

Widget build(BuildContext context) {
  return GestureDetector(
    child: Container(
      padding: EdgeInsets.fromLTRB(0.10.0.10.0.10.0),
      child: Row(
        children: [
          Hero(
            tag: heroTag,
            createRectTween: (begin, end) {
              returnListToDetailRectTween( begin: begin! , end: end! ,); }, child: ListImage( assetImageName: assetImageName, imageBgColor: imageBgColor, ), ), SizedBox( width:10.0,
          ),
          Expanded(
            child: Text(
              content,
              style: TextStyle(
                color: Colors.black87,
                fontSize: 18.0,
              ),
              maxLines: 2,
            ),
          ),
        ],
      ),
    ),
    onTap: () {
      Navigator.of(context).push(
        MaterialPageRoute(
          fullscreenDialog: true, builder: (context) => ListDetail( heroTag: heroTag, imageBgColor: imageBgColor, assetImageName: assetImageName, ), ), ); }); }Copy the code

In the head section, a ListImage component is isolated because it involves the processing of the border arc of the background. The entire image is a Stack component, and the bottom background Container with the arc to the right is done by the BoxDecoration. The following OvalImage is a circular Image wrapper class. In fact, the Image component can be wrapped with ClipOval. There is no code attached here.

Widget build(BuildContext context) {
  return Stack(
    children: [
      Container(
        height: 90.0,
        width: 110,
        decoration: BoxDecoration(
          color: imageBgColor,
          borderRadius: BorderRadius.only(
            topRight: Radius.circular(45.0),
            bottomRight: Radius.circular(45.0),
          ),
        ),
      ),
      Positioned(
        child: OvalImage(
          assetImageName: assetImageName,
          imageSize: 90.0,
        ),
        left: 20.0,
        top: 0.0,),); }Copy the code

The key here is to wrap the avatar area with Hero and then use createRectTween to define the Hero flight path.

Details page

The details page is a full screen popup, so you need to complete the operation of the back button. In fact, there is a Stack component at the top of the detail page. The background color and picture of the component remain the same as the list, and then add a close button and a detail title to the Stack component. To keep the status bar covered at the top of the page, the CustomScrollView implementation is used (see this article for more interesting sliders using CustomScrollView). At the top of the screen we define a ListDetailHeader component as shown below.

class ListDetailHeader extends StatelessWidget {
  final heroTag;
  final imageBgColor;
  final assetImageName;
  const ListDetailHeader({
    Key? key,
    required this.heroTag,
    required this.imageBgColor,
    required this.assetImageName,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Hero(
      child: Stack(
        children: [
          Container(
            height: 160.0,
            width: double.infinity,
            decoration: BoxDecoration(
              color: imageBgColor,
            ),
          ),
          Positioned(
            child: Material(
              child: IconButton(
                icon: Icon(
                  Icons.close,
                  color: Colors.white,
                ),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
              color: Colors.transparent,
            ),
            left: 10.0,
            top: 50.0,
            height: 40.0,
          ),
          Positioned(
            child: OvalImage(
              assetImageName: assetImageName,
              imageSize: 90.0,
            ),
            right: 20.0,
            top: 50.0,
          ),
          Positioned(
            child: Material(
              color: Colors.transparent,
              child: Text(
                'Here are the details.',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 18.0,
                ),
                maxLines: 1,
              ),
            ),
            left: 20,
            top: 100,
          ),
        ],
      ),
      tag: heroTag,
      createRectTween: (begin, end) {
        returnListToDetailRectTween( begin: begin! , end: end! ,); }); }}Copy the code

There is nothing special here, but at the beginning I had a problem with two underscores under the text, and the style was wrong. Later we found that the Scaffold used for fullscreenDialog was actually using Apple-style pages. To maintain the Material style, we need to use Scaffold or Material component wrap. Therefore, a Material component is added on top of the Text component. The Material component itself has a white background color. In order not to affect the background color of the component, you need to set its background color to Transparent.

The Hero component’s tag and createRectTween are the same as the list. The actual tag can be set using the id of the list element.

The source code

Dart /hero/list_to_detail_hero.dart

conclusion

This article describes a real-world application scenario for a Hero component — transitioning from list to detail using Hero. In fact, for better animation, you can combine AnimatedWidget, AnimatedBuilder, etc.

I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder.

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!