Ios 颤振-带有搜索栏的自定义条子应用程序栏

Ios 颤振-带有搜索栏的自定义条子应用程序栏,ios,flutter,dart,appbar,flutter-sliver,Ios,Flutter,Dart,Appbar,Flutter Sliver,我想有一个自定义的银appBar与搜索栏在它。我制作了一个普通的应用程序条,看起来是这样的:但我希望当我们向下滚动时,应用程序条看起来是这样的: 实际上,普通应用程序栏的代码只是一个绿色的AppBar,位于elevation:0的下方,我添加了我的标题()。以下是我的标题代码: 类头扩展StatefulWidget{ 字符串标题; Iconda图标; 标题({@required this.title,@required this.icon}); @凌驾 _HeaderState createS

我想有一个自定义的银appBar与搜索栏在它。我制作了一个普通的应用程序条,看起来是这样的:但我希望当我们向下滚动时,应用程序条看起来是这样的:

实际上,普通应用程序栏的代码只是一个绿色的
AppBar
,位于
elevation:0
的下方,我添加了我的
标题()。以下是我的标题代码:

类头扩展StatefulWidget{
字符串标题;
Iconda图标;
标题({@required this.title,@required this.icon});
@凌驾
_HeaderState createState()=>U HeaderState();
}
类_HeaderState扩展状态{
TextEditingController\u editingController;
@凌驾
void initState(){
super.initState();
_editingController=TextEditingController();
}
@凌驾
小部件构建(构建上下文){
Size Size=MediaQuery.of(context).Size;
返回首选大小(
首选大小:大小,
子:容器(
边距:仅限边集(底部:kDefaultPadding*2.5),
高度:尺寸。高度*0.2,
子:堆栈(
儿童:[
容器(
高度:尺寸。高度*0.2-27,
宽度:size.width,
装饰:盒子装饰(
颜色:主题。背景。原色,
borderRadius:仅限borderRadius(
左下角:半径。圆形(36),
右下角:半径。圆形(36),
)
),
子对象:对齐(
对齐:alignment.topCenter,
孩子:排(
mainAxisAlignment:mainAxisAlignment.center,
儿童:[
文本(widget.title,style:Theme.of(context).textTheme.headline4.copyWith(color:Colors.white,fontwweight:fontwweight.bold)),
尺寸箱(宽度:20,),
图标(widget.Icon,大小:40,颜色:Colors.white,)
],
)),
),
定位(
底部:0,
左:0,,
右:0,,
子:容器(
对齐:对齐.center,
边距:边集。对称(水平:kDefaultPadding),
填充:边集。对称(水平:kDefaultPadding),
身高:54,
装饰:盒子装饰(
颜色:颜色,白色,
边界半径:边界半径。圆形(20),
boxShadow:[boxShadow(
偏移量:偏移量(0,10),
半径:50,
颜色:主题。背景。原色。不透明度(0.23),
)]
),
孩子:排(
儿童:[
扩大(
孩子:TextField(
控制器:\ u编辑控制器,
textAlignVertical:textAlignVertical.center,
一旦更改:()=>setState((){}),
装饰:输入装饰(
hintText:'搜索',
hintStyle:TextStyle(颜色:Theme.of(context).primaryColor.withOpacity(0.5)),
enabledBorder:InputBorder.none,
FocusedOrder:InputBorder.none,
),
),
),
_editingController.text.trim().isEmpty?图标按钮(
图标:图标(Icons.search,颜色:Theme.of(context).primaryColor.withOpacity(0.5)),
onPressed:null):
图标按钮(
highlightColor:Colors.transparent,
splashColor:Colors.transparent,
图标:图标(Icons.clear,color:Theme.of(context.primaryColor.withOpacity(0.5)),
按下时:()=>设置状态(){
_editingController.clear();
})),
],
),
),
)
],
),
),
);
}
@凌驾
无效处置(){
_editingController.dispose();
super.dispose();
}
}

欢迎提供任何帮助。

我已经制作了一个简单的示例来展示主要逻辑

创建自己的
SliverPersistentHeaderDelegate
并计算收缩系数

import'dart:math';
进口“包装:颤振/材料.省道”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:“颤振演示”,
主题:主题数据(
主样本:颜色。蓝色,
),
主页:MyHomePage(),
);
}
}
类MyHomePage扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回脚手架(
背景颜色:Colors.white70,
正文:自定义滚动视图(
条子:[
滑冰机(
对,,
浮动:假,
代表:SearchHeader(
图标:Icons.terrain,
标题:"树木",,
搜索:_search(),
),
),
剩余碎片(
哈斯克罗博迪:没错,
子:ListView(
物理学:NeverscrollableScroll物理学(),
儿童:[
文本(“某些文本”),
占位符(
颜色:颜色,红色,
后备高度:200,
),
容器(
颜色:颜色。蓝灰色,
身高:500,
)
],
),
)
],
),
);
}
}
类搜索扩展了StatefulWidget{
_搜索({Key}):超级(Key:Key);
@凌驾
__SearchState createState()=>\u搜索
import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white70,
      body: CustomScrollView(
        slivers: [
          SliverPersistentHeader(
            pinned: true,
            floating: false,
            delegate: SearchHeader(
              icon: Icons.terrain,
              title: 'Trees',
              search: _Search(),
            ),
          ),
          SliverFillRemaining(
            hasScrollBody: true,
            child: ListView(
              physics: NeverScrollableScrollPhysics(),
              children: [
                Text('some text'),
                Placeholder(
                  color: Colors.red,
                  fallbackHeight: 200,
                ),
                Container(
                  color: Colors.blueGrey,
                  height: 500,
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}

class _Search extends StatefulWidget {
  _Search({Key key}) : super(key: key);

  @override
  __SearchState createState() => __SearchState();
}

class __SearchState extends State<_Search> {
  TextEditingController _editingController;

  @override
  void initState() {
    super.initState();
    _editingController = TextEditingController();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(left: 20, right: 5),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Expanded(
            child: TextField(
              controller: _editingController,
              // textAlignVertical: TextAlignVertical.center,
              onChanged: (_) => setState(() {}),
              decoration: InputDecoration(
                hintText: 'Search',
                hintStyle: TextStyle(
                    color: Theme.of(context).primaryColor.withOpacity(0.5)),
                enabledBorder: InputBorder.none,
                focusedBorder: InputBorder.none,
              ),
            ),
          ),
          _editingController.text.trim().isEmpty
              ? IconButton(
                  icon: Icon(Icons.search,
                      color: Theme.of(context).primaryColor.withOpacity(0.5)),
                  onPressed: null)
              : IconButton(
                  highlightColor: Colors.transparent,
                  splashColor: Colors.transparent,
                  icon: Icon(Icons.clear,
                      color: Theme.of(context).primaryColor.withOpacity(0.5)),
                  onPressed: () => setState(
                    () {
                      _editingController.clear();
                    },
                  ),
                ),
        ],
      ),
    );
  }
}

class SearchHeader extends SliverPersistentHeaderDelegate {
  final double minTopBarHeight = 100;
  final double maxTopBarHeight = 200;
  final String title;
  final IconData icon;
  final Widget search;

  SearchHeader({
    @required this.title,
    this.icon,
    this.search,
  });

  @override
  Widget build(
    BuildContext context,
    double shrinkOffset,
    bool overlapsContent,
  ) {
    var shrinkFactor = min(1, shrinkOffset / (maxExtent - minExtent));

    var topBar = Positioned(
      top: 0,
      left: 0,
      right: 0,
      child: Container(
        alignment: Alignment.center,
        height:
            max(maxTopBarHeight * (1 - shrinkFactor * 1.45), minTopBarHeight),
        width: 100,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(title,
                style: Theme.of(context).textTheme.headline4.copyWith(
                    color: Colors.white, fontWeight: FontWeight.bold)),
            SizedBox(
              width: 20,
            ),
            Icon(
              icon,
              size: 40,
              color: Colors.white,
            )
          ],
        ),
        decoration: BoxDecoration(
            color: Colors.green,
            borderRadius: BorderRadius.only(
              bottomLeft: Radius.circular(36),
              bottomRight: Radius.circular(36),
            )),
      ),
    );
    return Container(
      height: max(maxExtent - shrinkOffset, minExtent),
      child: Stack(
        fit: StackFit.loose,
        children: [
          if (shrinkFactor <= 0.5) topBar,
          Align(
            alignment: Alignment.bottomCenter,
            child: Padding(
              padding: EdgeInsets.only(
                bottom: 10,
              ),
              child: Container(
                alignment: Alignment.center,
                child: search,
                width: 200,
                height: 50,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(20),
                    color: Colors.white,
                    boxShadow: [
                      BoxShadow(
                        offset: Offset(0, 10),
                        blurRadius: 10,
                        color: Colors.green.withOpacity(0.23),
                      )
                    ]),
              ),
            ),
          ),
          if (shrinkFactor > 0.5) topBar,
        ],
      ),
    );
  }

  @override
  double get maxExtent => 230;

  @override
  double get minExtent => 100;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}