Flutter中如何通过悬浮操作按钮调用子组件内的函数?
如何在HomeScreen的悬浮按钮中调用HomeBackground的switchTheme方法?
问题场景
页面背景由HomeBackground(对应代码中的_IconsBackground)组件管理,需要在HomeScreen的悬浮操作按钮中调用该组件的switchTheme()方法切换主题。
背景组件代码
class _IconsBackground extends StatefulWidget { final bool isLoading; final IconData firstIcon; const _IconsBackground({required this.firstIcon, required this.isLoading}); @override State<_IconsBackground> createState() => _IconsBackgroundState(); } class _IconsBackgroundState extends State<_IconsBackground> { bool isDarkMode = false; Color iconsColor = AppTheme.primary; List<Color> background = [ const Color.fromARGB(255, 234, 231, 231), Colors.grey ]; void switchTheme() { isDarkMode = !isDarkMode; if (isDarkMode) { iconsColor = Colors.white; background = const [ Color.fromARGB(255, 64, 64, 64), Color.fromARGB(255, 0, 0, 0) ]; } else { iconsColor = AppTheme.primary; background = [const Color.fromARGB(255, 234, 231, 231), Colors.grey]; } setState(() {}); } @override Widget build(BuildContext context) { return Container(// 省略其他代码); } }
HomeScreen页面代码
class HomeScreen extends StatelessWidget { const HomeScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final BlogsProvider provider = Provider.of<BlogsProvider>(context); return Scaffold( appBar: const BlogAppBar(), floatingActionButton: const _UserActionsButton(), body: HomeBackground( firstIcon: Icons.chat, isLoading: false, child: RefreshIndicator( onRefresh: () async { provider.getBlogs(); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( children: _loadBlogs(provider), ), ), ))); } List<BlogCard> _loadBlogs(BlogsProvider provider) { List<Blog> blogsSorted = provider.blogs; blogsSorted.sort((a, b) => a.created.compareTo(b.created)); return blogsSorted.reversed.toList().map((e) => BlogCard(blog: e)).toList(); } }
解决方案
方案一:使用GlobalKey直接调用组件方法
适合快速实现,耦合性较高,适合小型场景:
- 修改HomeScreen为StatefulWidget并定义GlobalKey
class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { final GlobalKey<_IconsBackgroundState> _backgroundKey = GlobalKey(); @override Widget build(BuildContext context) { final BlogsProvider provider = Provider.of<BlogsProvider>(context); return Scaffold( appBar: const BlogAppBar(), floatingActionButton: _UserActionsButton( onThemeSwitch: () { _backgroundKey.currentState?.switchTheme(); }, ), body: _IconsBackground( key: _backgroundKey, firstIcon: Icons.chat, isLoading: false, child: RefreshIndicator( onRefresh: () async { provider.getBlogs(); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( children: _loadBlogs(provider), ), ), ))); } List<BlogCard> _loadBlogs(BlogsProvider provider) { List<Blog> blogsSorted = provider.blogs; blogsSorted.sort((a, b) => a.created.compareTo(b.created)); return blogsSorted.reversed.toList().map((e) => BlogCard(blog: e)).toList(); } }
- 修改_UserActionsButton接收回调
class _UserActionsButton extends StatelessWidget { final VoidCallback? onThemeSwitch; const _UserActionsButton({this.onThemeSwitch}); @override Widget build(BuildContext context) { return FloatingActionButton( onPressed: onThemeSwitch, child: const Icon(Icons.brightness_6), ); } }
方案二:将主题状态迁移到Provider(推荐)
项目已使用Provider,将主题状态放入Provider可实现解耦,符合架构设计:
- 在BlogsProvider中添加主题状态和方法
class BlogsProvider extends ChangeNotifier { // 原有代码... bool _isDarkMode = false; Color _iconsColor = AppTheme.primary; List<Color> _background = [ const Color.fromARGB(255, 234, 231, 231), Colors.grey ]; bool get isDarkMode => _isDarkMode; Color get iconsColor => _iconsColor; List<Color> get background => _background; void switchTheme() { _isDarkMode = !_isDarkMode; if (_isDarkMode) { _iconsColor = Colors.white; _background = const [ Color.fromARGB(255, 64, 64, 64), Color.fromARGB(255, 0, 0, 0) ]; } else { _iconsColor = AppTheme.primary; _background = [const Color.fromARGB(255, 234, 231, 231), Colors.grey]; } notifyListeners(); } // 原有方法... }
- 修改_IconsBackground为StatelessWidget
从Provider中获取状态,不再自行管理:
class _IconsBackground extends StatelessWidget { final bool isLoading; final IconData firstIcon; const _IconsBackground({required this.firstIcon, required this.isLoading}); @override Widget build(BuildContext context) { final BlogsProvider provider = Provider.of<BlogsProvider>(context); return Container( decoration: BoxDecoration( gradient: LinearGradient( colors: provider.background, begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), // 其他UI部分使用provider.iconsColor等状态 child: // 省略原有代码 ); } }
- 在悬浮按钮中调用Provider方法
class _UserActionsButton extends StatelessWidget { const _UserActionsButton(); @override Widget build(BuildContext context) { final BlogsProvider provider = Provider.of<BlogsProvider>(context, listen: false); return FloatingActionButton( onPressed: () { provider.switchTheme(); }, child: const Icon(Icons.brightness_6), ); } }
此时HomeScreen可保持StatelessWidget不变。
内容的提问来源于stack exchange,提问作者R.Ramirez
相关产品推荐
相关产品推荐

