Search code examples
flutteranimationtextwidgettransition

Text loses style during hero animation in Flutter


As you can see from the gif and from the code below I have a container and a text widget, both wrapped in a hero widget.

When the container is clicked the second page opens. I would like to have a hero animation for both widgets.

The animations of the container works great. The text however seems to lose the style while the transition is happening.

Any idea on how to fix it?

Tranition

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomeScreen(),
      routes: {
        HomeScreen.routName: (context) => const HomeScreen(),
        SecondSceen.routeName: (context) => const SecondSceen(),
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  static const routName = '/home-screen';

  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(children: [
        Align(
          alignment: Alignment.bottomCenter,
          child: InkWell(
            onTap: () => Navigator.of(context).pushNamed(SecondSceen.routeName),
            child: Hero(
              tag: 'box',
              child: Container(
                color: Colors.amber,
                width: MediaQuery.of(context).size.width * 0.8,
                height: 210,
              ),
            ),
          ),
        ),
        Align(
          alignment: Alignment.bottomCenter,
          child: SizedBox(
            width: MediaQuery.of(context).size.width * 0.8,
            height: 210,
            child: Padding(
              padding: const EdgeInsets.fromLTRB(16, 16, 8, 16),
              child: Column(
                children: const [
                  Hero(
                    tag: 'text',
                    child: Text(
                      "MY HEADER",
                      style:
                          TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ]),
    );
  }
}

class SecondSceen extends StatelessWidget {
  static const routeName = '/note-screen';

  const SecondSceen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        shadowColor: Colors.transparent,
        foregroundColor: Colors.black,
        centerTitle: true,
        title: const Hero(
          tag: 'text',
          child: Text(
            "MY HEADER",
            style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
          ),
        ),
      ),
      body: Stack(children: [
        Hero(
          tag: 'box',
          child: Container(
            color: Colors.amber,
          ),
        ),
      ]),
    );
  }
}

Solution

  • Instead of using individual TextStyle objects, use some style coming from the context. This way even during the animation, the styles will be based on the Theme of your MaterialApp.

    For both Text widgets, instead of:

    style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
    

    try:

    style: Theme.of(context).textTheme.headline6, // or some other style
    

    (You will need to remove const keywords because these are no longer constants.)

    You can easily customize any built in style using copyWith, for example:

    style: Theme.of(context).textTheme.headline6!.copyWith(
      fontSize: 28,
      fontWeight: FontWeight.bold),
    

    In general it is a good practice in my opinion to use Theme styles.