Flutter中Rive侧边栏菜单动画致首页报错:_sidebarAnim未初始化
问题核心原因
你遇到的错误本质是AnimatedBuilder未指定child参数,导致Widget构建时动画无法正确关联目标组件;同时菜单按钮的点击事件未绑定动画触发逻辑,_animationController的可空声明也增加了不必要的空安全复杂度。
修复步骤
- 给AnimatedBuilder添加child参数,确保动画与目标Widget正确关联
- 将菜单按钮的点击事件绑定到
onMenuPress方法,触发动画状态切换 - 去掉
_animationController的可空声明,简化空安全逻辑 - 修正
onMenuPress中的动画触发逻辑,根据菜单状态反向执行动画
修改后的完整代码
import 'package:INFLUFIT/MyProfile_business.dart'; import 'package:INFLUFIT/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/physics.dart'; import 'package:rive/rive.dart'; import 'post_detail.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:flutter/cupertino.dart'; import 'package:flashy_tab_bar2/flashy_tab_bar2.dart'; import 'Influencerprofile.dart'; import 'package:liquid_pull_to_refresh/liquid_pull_to_refresh.dart'; import 'add_content.dart'; import 'sidebar_menu.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with TickerProviderStateMixin { late AnimationController _animationController; late Animation<double> _sidebarAnim; late SMIBool _menuBtn; int _selectedIndex = 0; List<Widget> tabItems = [ Center(child: Text("0")), Center(child: Text("1")), Center(child: Text("2")), Center(child: Text("3")), Center(child: Text("4")) ]; final springDesc = const SpringDescription(mass: 0.1, stiffness: 40, damping: 5); void _onMenuIconInit(Artboard artboard) { final controller = StateMachineController.fromArtboard(artboard, "State Machine"); artboard.addController(controller!); _menuBtn = controller.findInput<bool>("isOpen") as SMIBool; _menuBtn.value = false; // 默认关闭状态,符合常规交互逻辑 } void onMenuPress() { setState(() { _menuBtn.value = !_menuBtn.value; }); // 根据菜单状态触发对应方向的弹簧动画 final targetValue = _menuBtn.value ? 1 : 0; final springAnim = SpringSimulation(springDesc, _sidebarAnim.value, targetValue, 0); _animationController.animateWith(springAnim); } @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 200), upperBound: 1, vsync: this, ); _sidebarAnim = Tween<double>(begin: 0, end: 1).animate( CurvedAnimation(parent: _animationController, curve: Curves.linear)); } Future<void> _handleRefresh() async { return await Future.delayed(Duration(seconds: 1)); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( automaticallyImplyLeading: false, backgroundColor: Colors.black, actions: <Widget>[ AnimatedBuilder( animation: _sidebarAnim, // 将菜单按钮作为child传入AnimatedBuilder child: GestureDetector( onTap: onMenuPress, // 绑定点击触发动画 child: Container( width: 70, height: 70, margin: const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(70 / 2), boxShadow: [ BoxShadow( color: RiveAppTheme.shadow.withOpacity(0.2), blurRadius: 5, offset: const Offset(0, 5)) ]), child: RiveAnimation.asset( '/Users/lilianasanda/influfit/assets/rive/menu_button.riv', stateMachines: const ["State Machine"], animations: const ["open", "close"], onInit: _onMenuIconInit, ), ), ), builder: (context, child) { return Transform.translate( offset: Offset(_sidebarAnim.value * 265, 0), child: child, ); }, ), ], title: Text( 'INFLUFIT.CLUB', style: GoogleFonts.bebasNeue( fontWeight: FontWeight.bold, fontSize: 30, color: Colors.white), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( bottom: Radius.circular(30), ), ), ), body: Padding( padding: const EdgeInsets.all(10.0), child: Column(children: [ Container( child: Stack(alignment: Alignment.bottomLeft, children: [ Align( alignment: Alignment.topCenter, child: Padding( padding: const EdgeInsets.all(10.0), child: TextField( readOnly: true, decoration: InputDecoration( hintText: "Search influencer", border: OutlineInputBorder( borderRadius: BorderRadius.circular(50), borderSide: BorderSide.none, ), fillColor: Colors.black.withOpacity(0.1), filled: true, ), ))) ])), SizedBox( height: 10, ), Container( alignment: Alignment.centerRight, child: Text( 'Location', style: TextStyle(), )), Expanded( child: LiquidPullToRefresh( onRefresh: _handleRefresh, color: Colors.black, child: ListView.builder( itemCount: tabItems.length, itemBuilder: (context, index) => PostDetail(), ), ), ), ]), ), bottomNavigationBar: FlashyTabBar( selectedIndex: _selectedIndex, showElevation: true, onItemSelected: (index) => setState(() { _selectedIndex = index; }), items: [ FlashyTabBarItem( icon: InkWell( child: Icon(Icons.home), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => HomePage()), ); }, ), title: Text('Feed'), ), FlashyTabBarItem( icon: Icon(Icons.favorite), title: Text('Favourites'), ), FlashyTabBarItem( icon: InkWell( child: Icon(Icons.add), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => ContentPage()), ); }, ), title: InkWell( child: Text('Add'), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => ContentPage()), ); }, ), ), FlashyTabBarItem( icon: Icon( Icons.notifications_active_outlined, ), title: Text('News'), ), FlashyTabBarItem( icon: InkWell( child: Icon(Icons.person), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => BusinessPage()), ); }, ), title: InkWell( child: Text('My profile'), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => BusinessPage()), ); }, ), ), ], ), ); } }
关键修复说明
- AnimatedBuilder添加child:解决了动画与目标组件未关联的核心问题,消除“未初始化”错误
- 绑定点击事件:让菜单按钮的点击操作真正触发动画状态切换
- 优化动画逻辑:根据菜单当前状态计算动画目标值,实现平滑的展开/收起过渡
- 简化控制器类型:去掉可空声明,避免不必要的空安全检查,让代码更简洁
内容的提问来源于stack exchange,提问作者sanda17
相关产品推荐
相关产品推荐

