ovi_b
ovi_b

Reputation: 169

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,
          ),
        ),
      ]),
    );
  }
}

Upvotes: 6

Views: 2784

Answers (4)

Tosin Peter
Tosin Peter

Reputation: 26

DefaultTextStyle(
                    style: AppTextStyle.subtitle1.copyWith(
                      fontWeight: AppFontWeight.medium,
                      color: AppColors.textColor,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    child: Text(
                      isCurrentUser ? currentUserState.username : userModel.username,
                    ),
                  ),

Upvotes: 0

Benjamin Ting
Benjamin Ting

Reputation: 709

It is a known Flutter issue. Someone posted a solution here. Good for anyone who may have the same question in the future: https://github.com/flutter/flutter/issues/30647#issuecomment-480980280

  Widget _flightShuttleBuilder(
    BuildContext flightContext,
    Animation<double> animation,
    HeroFlightDirection flightDirection,
    BuildContext fromHeroContext,
    BuildContext toHeroContext,
  ) {
    return DefaultTextStyle(
      style: DefaultTextStyle.of(toHeroContext).style,
      child: toHeroContext.widget,
    );
  }
  ...

Widget build(BuildContext context) {
  return Scaffold(
    ...
        child: Hero(
          tag: 'text',
          flightShuttleBuilder: _flightShuttleBuilder,
          child: Text('Text'),
        ),

Upvotes: 2

Pradip D.
Pradip D.

Reputation: 692

Just adding a Material as the child to Hero as shown below.

Hero(
  tag:'box',
  child: Material(// wrap with Material
    type: MaterialType.transparency, // likely needed
      child: Container(... // content

Happy Coding!!

Upvotes: 6

Peter Koltai
Peter Koltai

Reputation: 9789

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.

Upvotes: 10

Related Questions