flutter - 如何让我的应用栏在灵活应用栏的同时充当 material 搜索小部件

flutter - How can I make my appbar act as material search widget while being flexible app bar

我希望我的应用栏在向下滚动或用户搜索内容时充当固定的应用栏。

SliverAppBar(
  title: new TextField(
    style: Theme.of(context).primaryTextTheme.title,
    decoration: InputDecoration(
      hintText: '검색',
    ),
  ),
),


但我想在向上滚动且用户不搜索时将其绘制为灵活的应用栏。

flexibleSpace: new FlexibleSpaceBar(
  centerTitle: false,
  title: new TextField(
    style: Theme.of(context).primaryTextTheme.title,
    decoration: InputDecoration(
      hintText: '검색',
    ),
  ),
  background: Stack(
    fit: StackFit.expand,
    children: <Widget>[
      SizedBox(
        height: 256.0,
        child: Container(
          child: Padding(
            padding: const EdgeInsets.only(top: 24.0),
            child: Column(
              children: <Widget>[
                Align(
                  alignment: Alignment.topLeft,
                  child: FlutterLogo(
                    size: 64.0,
                  ),
                ),
                Padding(padding: const EdgeInsets.only(bottom: 24.0)),
                ListTile(
                  title: Text('Some Text'),
                ),
              ],
            ),
          ),
        ),
      ),
      // This gradient ensures that the toolbar icons are distinct
      // against the background image.
    ],
  ),
),

当使用第二种方法向上滚动时,搜索字段会稍微变换到右上角。

将title内容移动到另一个SliverList即可达到效果

从 SliverAppBar 中删除 flexibleSpace,并将 flexibleSpace.background 的内容移动到 SliverList 之前 SliverAppBar。

示例:

@override
Widget build(BuildContext context) {
  return new CustomScrollView(
    slivers: <Widget>[
      new SliverList(
          delegate: new SliverChildListDelegate(<Widget>[
        Align(
          alignment: Alignment.topLeft,
          child: FlutterLogo(size: 64.0),
        ),
        ListTile(
          title: Text('Some Text'),
        ),
        ListTile(),
      ])),
      new SliverAppBar(
        backgroundColor: Theme.of(context).scaffoldBackgroundColor,
        elevation: 0.0,
        automaticallyImplyLeading: false,
        pinned: true,
        floating: false,
        title: new TextField(
          focusNode: _searchFocusNode,
          style: Theme.of(context).primaryTextTheme.title,
          decoration: InputDecoration(
            border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
            suffixIcon: Icon(Icons.search),
            hintText: '검색',
          ),
        ),
      ),
      new SliverList(
        delegate: new SliverChildListDelegate(List.generate(
            100,
            (i) => ListTile(
                  title: Text('Scroll'),
                )).toList()),
      ),
    ],
  );
}

我在此 gist 中编写了一个 getMaterialSerach(); 方法,它具有您需要的确切 material 搜索视图。只需在您的 appBar: 小部件中添加 getMaterialSearch() ,如下所示。 here is the gist for getMaterialSearch();

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: getMaterialSearchBar(),
      body: Center(
        child: Container(),
      ),
    );
  }

这是我在 SliverAppBar 中使用 TextField 和 Tab 的代码:

NestedScrollView(
                    controller: model.mainScrollController,
                    headerSliverBuilder: (context, innerBoxIsScrolled) {
                      return [
                        /// https://github.com/flutter/flutter/issues/54059
                        SliverOverlapAbsorber(
                          handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                          sliver: SliverAppBar(
                            automaticallyImplyLeading: false,
                            pinned: true,
                            floating: true,
                            snap: true,
                            expandedHeight: 100,
                            flexibleSpace: FlexibleSpaceBar(
                              collapseMode: CollapseMode.pin,
                              background: Padding(
                                padding: const EdgeInsets.symmetric(vertical: 16.0),
                                child: Row(
                                  children: [
                                    BackButton(
                                      color: Colors.white,
                                    ),
                                    Flexible(
                                      child: TextField(
                                        controller: model.searchController,
                                        decoration: InputDecoration(
                                            focusColor: Colors.blueAccent,
                                            hoverColor: Colors.blueAccent,
                                            fillColor: Colors.white,
                                            filled: true,
                                            isDense: true,
                                            prefixIconConstraints: BoxConstraints(maxHeight: 24, maxWidth: 48),
                                            prefixIcon: Padding(
                                              padding: const EdgeInsets.symmetric(horizontal: 8.0),
                                              child: Icon(Icons.search_outlined),
                                            ),
                                            hintText: 'Search...'),
                                      ),
                                    ),
                                    IconButton(
                                      icon: Icon(
                                        Icons.add,
                                        color: Colors.white,
                                      ),
                                      onPressed: () {
                                        ExtendedNavigator.named('topNav').push(Routes.newExerciseView);
                                      },
                                      tooltip: 'Create Exercise',
                                    ),
                                  ],
                                ),
                              ),
                            ),
                            bottom: TabBar(
                              labelPadding: EdgeInsets.only(bottom: 8),
                              indicatorWeight: 3,
                              indicatorSize: TabBarIndicatorSize.label,
                              tabs: [
                                Text('All'),
                                Text('Custom'),
                                Text('Favorites'),
                              ],
                            ),
                          ),
                        ),
                      ];
                    },
                    body: TabBarView(
                        children: [
                      AllExercises(),
                      CustomExercises(),
                      FavoriteExercises(),
                    ]),
                  ),

有几个关键部分使这项工作正常进行,首先是您需要使用 SliverAppBar 的 flexibleSpace 属性 添加您的搜索小部件。如果您尝试将它添加到标题中 属性,TextField 只会被挤压但不会离开屏幕。

其次,确保 FlexibleSpaceBar 的 collapseMode 设置为 CollapseMode.pin。这使得 flexibleSpace 的内容滚出屏幕并且不会改变大小。

最后,将固定在 sliverAppBar 上的设置设置为 true,以便 TabBar 保持不变。