flutter 英雄小部件的子项在路由动画期间重复触发initState,没有键,

z6psavjg  于 5个月前  发布在  Flutter
关注(0)|答案(7)|浏览(61)

重现步骤

Flutter团队,
我遇到了一个问题,如果没有设置键,那么Hero小部件的子项在路由动画期间会重复触发initState。以下是详细信息:
重现步骤:
创建一个具有具有initState方法的子部件的Hero小部件。
不要为子部件设置键。
使用Hero动画导航到新路由。
观察在动画期间子部件的initState方法被多次触发。

预期结果

当子部件首次创建时,initState方法应该只被调用一次。

实际结果

在路由动画期间,initState方法被多次调用,导致意外行为。

代码示例

代码示例

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Transition Demo',
      home: MainScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Main Screen'),
      ),
      body: GestureDetector(
        onTap: () {
          Navigator.push(context, MaterialPageRoute(builder: (context) {
            return DetailScreen();
          }));
        },
        child: Hero(
          tag: 'imageHero',
          child: TestWidget(
            tag: 'MainScreen',
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: () {
          Navigator.pop(context);
        },
        child: Center(
          child: Hero(
            tag: 'imageHero',
            child: TestWidget(
              tag: 'DetailScreen',
            ),
          ),
        ),
      ),
    );
  }
}

class TestWidget extends StatefulWidget {
  const TestWidget({super.key, required this.tag});

  final String tag;

  @override
  State<TestWidget> createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidget> {
  @override
  void initState() {
    super.initState();
    print('initState ${widget.tag} ${this.hashCode}');
  }

  @override
  Widget build(BuildContext context) {
    return Image.network(
      'https://picsum.photos/250?image=9',
    );
  }
}

日志

日志

Launching lib/main.dart on sdk gphone64 arm64 in debug mode...
Running Gradle task 'assembleDebug'...
✓ Built build/app/outputs/flutter-apk/app-debug.apk
Installing build/app/outputs/flutter-apk/app-debug.apk...
Debug service listening on ws://127.0.0.1:53582/nkyWNoVtFJ0=/ws
Syncing files to device sdk gphone64 arm64...
I/flutter ( 3001): initState MainScreen 649717867
D/EGL_emulation( 3001): app_time_stats: avg=1109.82ms min=21.97ms max=2197.66ms count=2
I/flutter ( 3001): initState DetailScreen 743079764
W/WindowOnBackDispatcher( 3001): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher( 3001): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
D/EGL_emulation( 3001): app_time_stats: avg=4202.80ms min=4202.80ms max=4202.80ms count=1
I/flutter ( 3001): initState DetailScreen 458387907
I/flutter ( 3001): initState DetailScreen 505844363

Flutter Doctor输出

Doctor输出

[!] Flutter (Channel stable, 3.22.2, on macOS 13.5.1 22G90 darwin-arm64, locale zh-Hans-CN)
• Flutter version 3.22.2 on channel stable at /Users/cd/fvm/versions/stable
! The flutter binary is not on your path. Consider adding /Users/cd/fvm/versions/stable/bin to your path.
! The dart binary is not on your path. Consider adding /Users/cd/fvm/versions/stable/bin to your path.
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 761747bfc5 (8 days ago), 2024-06-05 22:15:13 +0200
• Engine revision edd8546116
• Dart version 3.4.3
• DevTools version 2.34.3
• If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
• Android SDK at /Users/cd/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11572160)
• All Android licenses accepted.

[!] Xcode - develop for iOS and macOS (Xcode 14.3.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 14E300c
! Flutter recommends a minimum Xcode version of 15.
Download the latest version or update via the Mac App Store.
✗ CocoaPods not installed.
CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
Without CocoaPods, plugins will not work on iOS or macOS.
For more info, see https://flutter.dev/platform-plugins
To install see https://guides.cocoapods.org/using/getting-started.html#installation for instructions.

[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2023.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11572160)

[✓] VS Code (version 1.90.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.90.0

[✓] Connected device (4 available)
• sdk gphone64 arm64 (mobile)     • emulator-5554         • android-arm64  • Android 14 (API 34) (emulator)
• macOS (desktop)                 • macos                 • darwin-arm64   • macOS 13.5.1 22G90 darwin-arm64
• Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin         • macOS 13.5.1 22G90 darwin-arm64
• Chrome (web)                    • chrome                • web-javascript • Google Chrome 126.0.6478.61

[✓] Network resources
• All expected network resources are available.
ngynwnxp

ngynwnxp1#

我使用Flutter 3.7.12运行代码示例,得到了相同的结果。我不确定这是否是有意为之。

z2acfund

z2acfund2#

@路人甲
key 传递给 Hero widget 也会导致相同的行为。您可以在这里阅读关于 initState 的工作原理:https://api.flutter.dev/flutter/widgets/State/initState.html,以了解它是否有助于理解这种行为。

tag5nh1u

tag5nh1u3#

final _key = GlobalKey();
Passing _key to TestWidget can fix it , my question is whether this is due to my incorrect usage or a design flaw in the Hero widget.

gg0vcinb

gg0vcinb4#

@darshankawar
这是因为Hero小部件使用了Overlay,导致子元素被反复插入到不同的树中吗?

llew8vvj

llew8vvj5#

我的疑问是这是否是由于我使用不当,还是Hero小部件的设计缺陷。

检查Hero小部件的使用/实现的官方文档,看看它是否对你的情况有帮助。

2izufjch

2izufjch6#

我的问题是这是否是由于我的错误使用或者Hero小部件的设计缺陷导致的。
请查阅Hero小部件的使用/实现的官方文档,看看它是否能帮助你解决问题。
https://api.flutter.dev/flutter/widgets/Hero-class.htmlhttps://docs.flutter.dev/ui/animations/hero-animations
谢谢。我已经阅读了文档并审查了源代码,我理解为什么initState会被多次调用,但我仍然认为这是Hero小部件的一个缺陷。

k7fdbhmy

k7fdbhmy7#

感谢您的更新。请保持问题开放,以便团队跟踪。

相关问题