Flutter Material 3实现类似Google Play Books的全屏展开搜索栏方案
实现类似Google Play Books的全屏搜索展开效果(Material 3)
需求:实现Google Play Books中那种点击搜索栏后,搜索区域从AppBar内的小卡片平滑展开为全屏搜索界面的效果,当前已在AppBar的Card组件内嵌套了TextField,基于Material 3开发。
现有代码片段:
appBar: AppBar( title: Card( elevation: 2, child: TextField( autocorrect: false, controller: _textEditingController, onChanged: (value) { setState(() {}); }, decoration: InputDecoration( hintText: "Search", border: InputBorder.none, prefixIcon: const Icon( Icons.search, ), suffixIcon: _textEditingController.text.isEmpty ? null : IconButton( icon: const Icon(Icons.clear), onPressed: _clearTextField, ), ), ), ), )
实现步骤与完整代码
核心思路是通过状态变量控制搜索展开状态,结合动画切换AppBar和全屏搜索界面的显示:
- 添加状态变量管理展开状态
- 为搜索栏添加点击事件触发展开
- 构建自适应的AppBar和全屏搜索区域
- 添加平滑过渡动画提升体验
完整示例代码:
import 'package:flutter/material.dart'; class SearchScreen extends StatefulWidget { const SearchScreen({super.key}); @override State<SearchScreen> createState() => _SearchScreenState(); } class _SearchScreenState extends State<SearchScreen> with SingleTickerProviderStateMixin { final TextEditingController _textEditingController = TextEditingController(); bool _isSearchExpanded = false; late AnimationController _animationController; late Animation<double> _opacityAnimation; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _opacityAnimation = Tween<double>(begin: 1, end: 0).animate( CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), ); } void _clearTextField() { _textEditingController.clear(); setState(() {}); } void _toggleSearchExpansion() { setState(() { _isSearchExpanded = !_isSearchExpanded; if (_isSearchExpanded) { _animationController.forward(); // 延迟获取焦点,确保动画流畅 Future.delayed(const Duration(milliseconds: 200), () { FocusScope.of(context).requestFocus(FocusNode()); }); } else { _animationController.reverse(); FocusScope.of(context).unfocus(); } }); } @override void dispose() { _textEditingController.dispose(); _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: _isSearchExpanded ? AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: _toggleSearchExpansion, ), title: TextField( autocorrect: false, controller: _textEditingController, autofocus: true, onChanged: (value) { setState(() {}); }, decoration: const InputDecoration( hintText: "Search", border: InputBorder.none, ), ), actions: [ if (_textEditingController.text.isNotEmpty) IconButton( icon: const Icon(Icons.clear), onPressed: _clearTextField, ), ], ) : AppBar( title: GestureDetector( onTap: _toggleSearchExpansion, child: Card( elevation: 2, child: TextField( autocorrect: false, controller: _textEditingController, enabled: false, // 非展开状态仅允许点击触发展开 decoration: InputDecoration( hintText: "Search", border: InputBorder.none, prefixIcon: const Icon(Icons.search), suffixIcon: _textEditingController.text.isEmpty ? null : IconButton( icon: const Icon(Icons.clear), onPressed: _clearTextField, ), ), ), ), ), ), body: Stack( children: [ // 原页面内容,展开时渐隐 FadeTransition( opacity: _opacityAnimation, child: const Center( child: Text("原页面内容区域"), ), ), // 全屏搜索结果区域,展开时显示 if (_isSearchExpanded) Positioned.fill( child: Container( color: Theme.of(context).scaffoldBackgroundColor, child: ListView( padding: const EdgeInsets.all(16), children: [ // 搜索结果示例 ListTile( leading: const Icon(Icons.book), title: const Text("搜索结果1"), subtitle: const Text("书籍描述内容"), onTap: () {}, ), ListTile( leading: const Icon(Icons.book), title: const Text("搜索结果2"), subtitle: const Text("书籍描述内容"), onTap: () {}, ), ], ), ), ), ], ), ); } }
关键说明
- 非展开状态下,TextField设置为
enabled: false,确保只能通过点击Card触发展开,避免误操作 - 使用
AnimationController实现原页面内容的渐隐动画,让过渡更自然 - 展开时AppBar切换为带返回按钮的搜索栏,同时全屏显示搜索结果列表
- 处理焦点逻辑,确保展开时自动弹出键盘,收起时关闭键盘
内容的提问来源于stack exchange,提问作者graduationchampion
相关产品推荐
相关产品推荐

