<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
SliverAppBar的floating=true,pinned=false模式中巢狀的TextField,會在獲取焦點時觸發CustomScrollView捲動到頂部。
CustomScrollView和SliverAppBar的介紹和演示,參見官方檔案。
在floating=true和pinned=false 這兩個組合引數的模式下,SliverAppBar表現為:列表向上滑動時隨列表向上滑動直至消失。
列表在任何位置向下滑動時,會立即從上方滑入直至全部展現。
如果該元件內巢狀了TextField,在列表上滑一段距離,再下滑至SliverAppBar及其內巢狀的TextField出現時(此時列表尚未滑動到頂端),點選TextField使其獲取焦點以輸入文字,此時列表會立即捲動至頂。
如圖:
開始偵錯問題,嘗試了各種引數組合,只要pinned為true就沒有這個問題,因為SliverAppBar總會展現在最頂端。然後想到了在獲取焦點的同時,將CustomScrollView的physics設定為 NeverScrollableScrollPhysics(意為禁止捲動),此時並不影響CustomScrollView的捲動位置,然後在輸入完成或失去焦點時,再取消禁止捲動的狀態,即可避免獲取焦點時列表捲動至頂端的問題。解決程式碼如下:
class CustomScrollTextFieldPage extends StatefulWidget { const CustomScrollTextFieldPage({Key? key}) : super(key: key); @override State<CustomScrollTextFieldPage> createState() => _CustomScrollTextFieldPageState(); } class _CustomScrollTextFieldPageState extends State<CustomScrollTextFieldPage> { final textController = TextEditingController(); final editableTextController = TextEditingController(); bool focused = false; final focusNode = FocusNode(); final buttonFocus = FocusNode(); final textFocus = FocusNode(); @override void initState() { super.initState(); focusNode.addListener(_onFocus); } @override void dispose() { focusNode.removeListener(_onFocus); super.dispose(); } _onFocus() { setState(() { focused = focusNode.hasFocus; }); } @override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( behavior: HitTestBehavior.translucent, onTapDown: () { FocusManager.instance.rootScope.requestFocus(FocusNode()); }, child: CustomScrollView( physics: focused ? const NeverScrollableScrollPhysics() : null, slivers: <Widget>[ SliverAppBar( floating: true, pinned: false, expandedHeight: 250.0, flexibleSpace: FlexibleSpaceBar( expandedTitleScale: 1, title: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( child: TextField( focusNode: focusNode, controller: textController, onEditingComplete: () { FocusManager.instance.rootScope.requestFocus(FocusNode()); }, style: const TextStyle(color: Colors.white), decoration: const InputDecoration( border: UnderlineInputBorder( borderSide: BorderSide(color: Colors.white), ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.white), ), ), ), ), Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: IconButton( visualDensity: VisualDensity(horizontal: 0, vertical: -4), padding: EdgeInsets.zero, onPressed: () { print('btn clicked'); buttonFocus.requestFocus(); }, focusNode: buttonFocus, icon: Icon(Icons.heart_broken), ), ) ], ), ), ), SliverGrid( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200.0, mainAxisSpacing: 10.0, crossAxisSpacing: 10.0, childAspectRatio: 4.0, ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Container( alignment: Alignment.center, color: Colors.teal[100 * (index % 9)], child: Text('Grid Item $index'), ); }, childCount: 20, ), ), SliverFixedExtentList( itemExtent: 50.0, delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Container( alignment: Alignment.center, color: Colors.lightBlue[100 * (index % 9)], child: Text('List Item $index'), ); }, ), ), ], ), ), ); } }
這個解決方法有點不完美的表現,就是輸入完成時不點選頁面,而是直接點選收起鍵盤,這時不會觸發onTapDown也不會觸發 onEditingComplete ,就需要在螢幕再點選或者滑動時才能重置列表的可捲動狀態。
經過進一步測試,發現在輸入框內的EditableText中對focus進行了監聽,在獲取焦點時遞迴呼叫了RenderObject的showOnScreen方法,會一直向上追溯Render樹,最終呼叫到RenderSliverList中,觸發了捲動事件。
是不是可以在TextField外包裹一個自定義了RenderBox的元件,把這個showOnScreen呼叫給切斷呢?於是翻了下官方的幾個元件寫法,照貓畫虎寫了個自定義的元件
class IgnoreShowOnScreenWidget extends SingleChildRenderObjectWidget { const IgnoreShowOnScreenWidget({ Key? key, Widget? child, this.ignoreShowOnScreen = true, }) : super(key: key, child: child); final bool ignoreShowOnScreen; @override RenderObject createRenderObject(BuildContext context) { return IgnoreShowOnScreenRenderObject( ignoreShowOnScreen: ignoreShowOnScreen, ); } } class IgnoreShowOnScreenRenderObject extends RenderProxyBox { IgnoreShowOnScreenRenderObject({ RenderBox? child, this.ignoreShowOnScreen = true, }); final bool ignoreShowOnScreen; @override void showOnScreen({ RenderObject? descendant, Rect? rect, Duration duration = Duration.zero, Curve curve = Curves.ease, }) { if (!ignoreShowOnScreen) { return super.showOnScreen( descendant: descendant, rect: rect, duration: duration, curve: curve, ); } } }
使用方法
class CustomScrollTextFieldPage extends StatefulWidget { const CustomScrollTextFieldPage({Key? key}) : super(key: key); @override State<CustomScrollTextFieldPage> createState() => _CustomScrollTextFieldPageState(); } class _CustomScrollTextFieldPageState extends State<CustomScrollTextFieldPage> { final textController = TextEditingController(); final focusNode = FocusNode(); @override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { FocusManager.instance.rootScope.requestFocus(FocusNode()); }, child: CustomScrollView( slivers: <Widget>[ SliverAppBar( floating: true, pinned: false, expandedHeight: 250.0, flexibleSpace: FlexibleSpaceBar( expandedTitleScale: 1, title: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( child: IgnoreShowOnScreenWidget( child: TextField( focusNode: focusNode , controller: textController , style: const TextStyle(color: Colors.white), decoration: const InputDecoration( border: UnderlineInputBorder( borderSide: BorderSide(color: Colors.white), ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.white), ), ), ), ), ), Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: IconButton( visualDensity: VisualDensity(horizontal: 0, vertical: -4), padding: EdgeInsets.zero, onPressed: () { print('btn clicked'); }, icon: Icon(Icons.heart_broken), ), ) ], ), ), ), SliverGrid( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200.0, mainAxisSpacing: 10.0, crossAxisSpacing: 10.0, childAspectRatio: 4.0, ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Container( alignment: Alignment.center, color: Colors.teal[100 * (index % 9)], child: Text('Grid Item $index'), ); }, childCount: 20, ), ), SliverFixedExtentList( itemExtent: 50.0, delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Container( alignment: Alignment.center, color: Colors.lightBlue[100 * (index % 9)], child: Text('List Item $index'), ); }, ), ), ], ), ), ); } }
初步嘗試,確實可以更方便地解決問題。
效果如圖:
目前還未發現有什麼副作用,如果哪位大神有更好的解決辦法,
以上就是浮動AppBar中的textField焦點回滾問題解決的詳細內容,更多關於AppBar浮動textField焦點回滾的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45