Flutter 中具有搜索功能的 sliver 应用栏

jus*_*guy 3 search animation dart flutter

希望你有美好的一天。我想在下面实现这样的东西 => gif 图像 1

谁的 gif 不清楚。它是来自名为 的应用程序的屏幕截图Yelp。它是具有扩展和折叠功能的银色应用栏。当它折叠搜索栏固定到标题。无论如何,我已经完成了这个 => gif 图像 2

当我折叠 sliver 应用栏时,我的搜索栏正在缩小。我希望当我折叠 sliver 应用栏并修复上面标题中的搜索栏时,搜索不会缩小。这是我的代码

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  double changingHeight;
  double appBarHeight;
  bool appBarSearchShow = false;
  final TextEditingController _filter = new TextEditingController();

  List<String> itemList = [];

  @override
  void initState() {
    for (int count = 0; count < 50; count++) {
      itemList.add("Item $count");
    }
    changingHeight = 300;
  }

  @override
  Widget build(BuildContext context) {
    appBarHeight = MediaQuery.of(context).padding.top + kToolbarHeight;
    return Scaffold(
      backgroundColor: Colors.white,
      body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxScrolled) {
            return <Widget>[createSilverAppBar()];
          },
          body: ListView.builder(
              itemCount: itemList.length,
              itemBuilder: (context, index) {
                return Card(
                  child: ListTile(
                    title: Text(itemList[index]),
                  ),
                );
              })),
    );
  }

  SliverAppBar createSilverAppBar() {
    return SliverAppBar(
      backgroundColor: Colors.white,
      expandedHeight: 300,
      floating: false,
      pinned: true,
      // title: appBarSearchShow == true
      //     ? CupertinoTextField(
      //         controller: _filter,
      //         keyboardType: TextInputType.text,
      //         placeholder: "Search..",
      //         placeholderStyle: TextStyle(
      //           color: Color(0xffC4C6CC),
      //           fontSize: 14.0,
      //           fontFamily: 'Brutal',
      //         ),
      //         prefix: Padding(
      //           padding: const EdgeInsets.fromLTRB(9.0, 6.0, 9.0, 6.0),
      //           child: Icon(
      //             Icons.search,
      //           ),
      //         ),
      //         decoration: BoxDecoration(
      //           borderRadius: BorderRadius.circular(8.0),
      //           color: Colors.white,
      //         ),
      //       )
      //     : Container(),
      flexibleSpace: LayoutBuilder(
          builder: (BuildContext context, BoxConstraints constraints) {
        if (constraints.biggest.height == appBarHeight) {
          appBarSearchShow = true;
        } else {
          appBarSearchShow = false;
        }
        return FlexibleSpaceBar(
          collapseMode: CollapseMode.parallax,
          titlePadding: EdgeInsets.only(bottom: 10),
          centerTitle: true,
          title: constraints.biggest.height != appBarHeight
              ? Container(
                  //margin: EdgeInsets.symmetric(horizontal: 10),
                  constraints: BoxConstraints(minHeight: 30, maxHeight: 30),
                  width: 220,
                  decoration: BoxDecoration(
                    boxShadow: <BoxShadow>[
                      BoxShadow(
                          color: Colors.grey.withOpacity(0.6),
                          offset: const Offset(1.1, 1.1),
                          blurRadius: 5.0),
                    ],
                  ),
                  child: CupertinoTextField(
                    controller: _filter,
                    keyboardType: TextInputType.text,
                    placeholder: 'Search',
                    placeholderStyle: TextStyle(
                      color: Color(0xffC4C6CC),
                      fontSize: 14.0,
                      fontFamily: 'Brutal',
                    ),
                    prefix: Padding(
                      padding: const EdgeInsets.fromLTRB(5.0, 5.0, 0.0, 5.0),
                      child: Icon(
                        Icons.search,
                        size: 18,
                      ),
                    ),
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(8.0),
                      color: Colors.white,
                    ),
                  ),
                )
              : Container(),
          background: Container(
            //height: constraints.maxHeight - 15,
            color: Colors.white,
            margin: EdgeInsets.only(bottom: 30),
            child: Image.asset(
              'assets/mainBackImage.jpg',
              fit: BoxFit.cover,
            ),
          ),
        );
      }),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

任何帮助,将不胜感激。

Mob*_*ina 13

这是使搜索栏固定并阻止其缩小的解决方案:

您可以使用两个SilverAppBars,一个用于背景图像,一个用于搜索栏。第一个SilverAppBar没有标题和海拔,也没有固定。第二个SilverAppBar被固定并具有高度,其标题是SearchBar.

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxScrolled) {
            return <Widget>[
              createSilverAppBar1(),
              createSilverAppBar2()
            ];
          },
          body: ListView.builder(
              itemCount: itemList.length,
              itemBuilder: (context, index) {
                return Card(
                  child: ListTile(
                    title: Text(itemList[index]),
                  ),
                );
              })),
    );
  }

  SliverAppBar createSilverAppBar1() {
    return SliverAppBar(
      backgroundColor: Colors.redAccent,
      expandedHeight: 300,
      floating: false,
      elevation: 0,
      flexibleSpace: LayoutBuilder(
          builder: (BuildContext context, BoxConstraints constraints) {
            return FlexibleSpaceBar(
              collapseMode: CollapseMode.parallax,
              background: Container(
                color: Colors.white,
                child: Image.asset(
                  'assets/mainBackImage.jpg',
                  fit: BoxFit.cover,
                ),
              ),
            );
          }),
    );
  }

  SliverAppBar createSilverAppBar2() {
    return SliverAppBar(
      backgroundColor: Colors.redAccent,
      pinned: true,
      title: Container(
        margin: EdgeInsets.symmetric(horizontal: 10),
        height: 40,
        decoration: BoxDecoration(
          boxShadow: <BoxShadow>[
            BoxShadow(
                color: Colors.grey.withOpacity(0.6),
                offset: const Offset(1.1, 1.1),
                blurRadius: 5.0),
          ],
        ),
        child: CupertinoTextField(
          controller: _filter,
          keyboardType: TextInputType.text,
          placeholder: 'Search',
          placeholderStyle: TextStyle(
            color: Color(0xffC4C6CC),
            fontSize: 14.0,
            fontFamily: 'Brutal',
          ),
          prefix: Padding(
            padding: const EdgeInsets.fromLTRB(5.0, 5.0, 0.0, 5.0),
            child: Icon(
              Icons.search,
              size: 18,
              color: Colors.black,
            ),
          ),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8.0),
            color: Colors.white,
          ),
        ),
      ),
    );
  }
Run Code Online (Sandbox Code Playgroud)

结果:

资源

这是基于gif 图像 1进行布局的解决方案

使用Stack您可以使搜索栏堆叠在背景之上。搜索栏的偏移量是expandedHeight - shrinkOffset - 20因为它应该取决于应用栏缩小的程度以及未缩小时应用栏的总高度。20 是搜索栏高度的一半,减去它以使搜索栏向上移动其高度的一半。

@override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxScrolled) {
            return <Widget>[
              SliverPersistentHeader(
                delegate: MySliverAppBar(expandedHeight: 200, filter: _filter),
                pinned: true,
              ),
            ];
          },
          body: ListView.builder(
              itemCount: itemList.length,
              itemBuilder: (context, index) {
                return Card(
                  child: ListTile(
                    title: Text(itemList[index]),
                  ),
                );
              })),
    );
  }

class MySliverAppBar extends SliverPersistentHeaderDelegate {
  final double expandedHeight;
  final TextEditingController filter;
  MySliverAppBar({@required this.expandedHeight, @required this.filter});
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    var searchBarOffset = expandedHeight - shrinkOffset - 20;
    return Stack(
      fit: StackFit.expand,
      overflow: Overflow.visible,
      children: [
        Container(
          child: Image.network(
            'assets/mainBackImage.jpg',
            fit: BoxFit.cover,
          ),
        ),
        (shrinkOffset < expandedHeight - 20) ? Positioned(
          top: searchBarOffset,
          left: MediaQuery.of(context).size.width / 4,
          child: Card(
            elevation: 10,
            child: SizedBox(
            height: 40,
            width: MediaQuery.of(context).size.width / 2,
            child: CupertinoTextField(
              controller: filter,
              keyboardType: TextInputType.text,
              placeholder: 'Search',
              placeholderStyle: TextStyle(
                color: Color(0xffC4C6CC),
                fontSize: 14.0,
                fontFamily: 'Brutal',
              ),
              prefix: Padding(
                padding: const EdgeInsets.fromLTRB(5.0, 5.0, 0.0, 5.0),
                child: Icon(
                  Icons.search,
                  size: 18,
                  color: Colors.black,
                ),
              ),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8.0),
                color: Colors.white,
              ),
            ),
          ),
          ),
        ) : Container(
          margin: EdgeInsets.symmetric(
              horizontal: MediaQuery.of(context).size.width / 4,
              vertical: (kToolbarHeight - 40) / 4
          ),
          child: Card(
            elevation: 10,
            child: CupertinoTextField(
              controller: filter,
              keyboardType: TextInputType.text,
              placeholder: 'Search',
              placeholderStyle: TextStyle(
                color: Color(0xffC4C6CC),
                fontSize: 14.0,
                fontFamily: 'Brutal',
              ),
              prefix: Padding(
                padding: const EdgeInsets.fromLTRB(5.0, 5.0, 0.0, 5.0),
                child: Icon(
                  Icons.search,
                  size: 18,
                  color: Colors.black,
                ),
              ),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8.0),
                color: Colors.white,
              ),
            ),
          ),
        ),
      ],
    );
  }

  @override
  double get maxExtent => expandedHeight;

  @override
  double get minExtent => kToolbarHeight;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
Run Code Online (Sandbox Code Playgroud)

结果:

资源2