Flutter 颤振中的标准底板

Flutter 颤振中的标准底板,flutter,bottom-sheet,Flutter,Bottom Sheet,我很难在我的应用程序中实现“标准底部工作表”-我指的是底部工作表,其中“标题”可见且可拖动(参考:)。更重要的是:我在任何地方都找不到这样的例子:S.我所希望的结果是通过实现DragableScrollableSheet作为底部工作表来实现的:在Scaffold中(只有该小部件具有initialChildSize),但是没有办法使标题“粘滞”bc所有内容都是可滚动的:/ 我还发现:-像那里的接缝关于“持久的底部工作表”的部分是我正在寻找的,但文章没有详细说明,所以我无法准确地找出实现它的方法,加

我很难在我的应用程序中实现“标准底部工作表”-我指的是底部工作表,其中“标题”可见且可拖动(参考:)。更重要的是:我在任何地方都找不到这样的例子:S.我所希望的结果是通过实现DragableScrollableSheet作为底部工作表来实现的:在Scaffold中(只有该小部件具有initialChildSize),但是没有办法使标题“粘滞”bc所有内容都是可滚动的:/

我还发现:-像那里的接缝关于“持久的底部工作表”的部分是我正在寻找的,但文章没有详细说明,所以我无法准确地找出实现它的方法,加上评论通常是负面的,所以我猜这不是完全正确的


有人有任何解决方案吗?:S

如果您正在寻找持久的底部表单,请参考下面链接中的源代码


您可以参考\u showBottomSheet()了解您的需求,一些更改将满足您的需求

您可以使用堆栈和动画:


class HelloWorldPage extends StatefulWidget {
  @override
  _HelloWorldPageState createState() => _HelloWorldPageState();
}

class _HelloWorldPageState extends State<HelloWorldPage>
    with SingleTickerProviderStateMixin {
  final double minSize = 80;
  final double maxSize = 350;

  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 500))
          ..addListener(() {
            setState(() {});
          });

    _animation =
        Tween<double>(begin: minSize, end: maxSize).animate(_controller);

    super.initState();
  }

  AnimationController _controller;
  Animation<double> _animation;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          Positioned(
            bottom: 0,
            height: _animation.value,
            child: GestureDetector(
              onDoubleTap: () => _onEvent(),
              onVerticalDragEnd: (event) => _onEvent(),
              child: Container(
                color: Colors.red,
                width: MediaQuery.of(context).size.width,
                height: minSize,
              ),
            ),
          ),
        ],
      ),
    );
  }

  _onEvent() {
    if (_controller.isCompleted) {
      _controller.reverse(from: maxSize);
    } else {
      _controller.forward();
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}


类HelloWorldPage扩展StatefulWidget{
@凌驾
_HelloWorldPageState createState()=>\u HelloWorldPageState();
}
类_HelloWorldPageState扩展了状态
使用SingleTickerProviderStateMixin{
最终双分钟=80;
最终双倍最大尺寸=350;
void initState(){
_控制器=
AnimationController(vsync:this,duration:duration(毫秒:500))
…addListener(){
setState((){});
});
_动画=
Tween(开始:minSize,结束:maxSize).animate(_controller);
super.initState();
}
动画控制器_控制器;
动画(动画),;
@凌驾
小部件构建(构建上下文){
返回脚手架(
主体:堆栈(
fit:StackFit.expand,
儿童:[
定位(
底部:0,
高度:_animation.value,
儿童:手势检测器(
onDoubleTap:()=>\u OneEvent(),
onVerticalDragEnd:(事件)=>\u onEvent(),
子:容器(
颜色:颜色,红色,
宽度:MediaQuery.of(context).size.width,
高度:minSize,
),
),
),
],
),
);
}
_onEvent(){
如果(_controller.isCompleted){
_控制器。反向(从:最大尺寸);
}否则{
_controller.forward();
}
}
@凌驾
无效处置(){
_controller.dispose();
super.dispose();
}
}

正如@Sergio提出的一些好的替代方案一样,它仍然需要更多的编码才能正常工作,尽管如此,我发现了向上滑动面板,因此任何其他寻求解决方案的人都可以找到它


尽管如此,我还是觉得很奇怪,Flatter中的内置底部表单小部件没有提供创建material.io:S中提到的“标准底部表单”的选项。可以使用
DragableScrollableSheet
实现在material spec中看到的标准底部表单行为

在这里我将详细解释它

步骤1: 定义您的
脚手架

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Draggable sheet demo',
      home: Scaffold(

          ///just for status bar color.
          appBar: PreferredSize(
              preferredSize: Size.fromHeight(0),
              child: AppBar(
                primary: true,
                elevation: 0,
              )),
          body: Stack(
            children: <Widget>[
              Positioned(
                left: 0.0,
                top: 0.0,
                right: 0.0,
                child: PreferredSize(
                    preferredSize: Size.fromHeight(56.0),
                    child: AppBar(
                      title: Text("Standard bottom sheet demo"),
                      elevation: 2.0,
                    )),
              ),
              DraggableSearchableListView(),
            ],
          )),
    );
  }
}
步骤3: 定义自定义粘性搜索栏

 class SearchBar extends StatelessWidget {
  final TextEditingController textEditingController;
  final ValueNotifier<bool> closeButtonVisibility;
  final ValueChanged<String> onSearchSubmit;
  final VoidCallback onClose;

  const SearchBar({
    Key key,
    @required this.textEditingController,
    @required this.closeButtonVisibility,
    @required this.onSearchSubmit,
    @required this.onClose,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    return Container(
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 0),
        child: Row(
          children: <Widget>[
            SizedBox(
              height: 56.0,
              width: 56.0,
              child: Material(
                type: MaterialType.transparency,
                child: InkWell(
                  child: Icon(
                    Icons.arrow_back,
                    color: theme.textTheme.caption.color,
                  ),
                  onTap: () {
                    FocusScope.of(context).unfocus();
                    textEditingController.clear();
                    closeButtonVisibility.value = false;
                    onClose();
                  },
                ),
              ),
            ),
            SizedBox(
              width: 16.0,
            ),
            Expanded(
              child: TextFormField(
                onChanged: (value) {
                  if (value != null && value.length > 0) {
                    closeButtonVisibility.value = true;
                  } else {
                    closeButtonVisibility.value = false;
                  }
                },
                onFieldSubmitted: (value) {
                  FocusScope.of(context).unfocus();
                  onSearchSubmit(value);
                },
                keyboardType: TextInputType.text,
                textInputAction: TextInputAction.search,
                textCapitalization: TextCapitalization.none,
                textAlignVertical: TextAlignVertical.center,
                textAlign: TextAlign.left,
                maxLines: 1,
                controller: textEditingController,
                decoration: InputDecoration(
                  isDense: true,
                  border: InputBorder.none,
                  hintText: "Search here",
                ),
              ),
            ),
            ValueListenableBuilder<bool>(
                valueListenable: closeButtonVisibility,
                builder: (context, value, child) {
                  return value
                      ? SizedBox(
                          width: 56.0,
                          height: 56.0,
                          child: Material(
                            type: MaterialType.transparency,
                            child: InkWell(
                              child: Icon(
                                Icons.close,
                                color: theme.textTheme.caption.color,
                              ),
                              onTap: () {
                                closeButtonVisibility.value = false;
                                textEditingController.clear();
                              },
                            ),
                          ),
                        )
                      : Container();
                })
          ],
        ),
      ),
    );
  }
}
类搜索栏扩展了无状态小部件{
最终文本编辑控制器文本编辑控制器;
最终价值通知人关闭按钮可视性;
搜索提交时更改的最终值;
最终失效;
常量搜索栏({
关键点,
@需要此.textEditingController,
@需要此选项。关闭按钮不可见,
@需要此.onSearchSubmit,
@需要这个.onClose,
}):super(key:key);
@凌驾
小部件构建(构建上下文){
最终主题数据主题=theme.of(上下文);
返回容器(
孩子:填充(
填充:边集。对称(水平:0),
孩子:排(
儿童:[
大小盒子(
身高:56.0,
宽度:56.0,
儿童:材料(
类型:MaterialType.transparency,
孩子:InkWell(
子:图标(
Icons.arrow_back,
颜色:theme.textTheme.caption.color,
),
onTap:(){
(上下文)的焦点范围。取消焦点();
textEditingController.clear();
closeButtonVisibility.value=false;
onClose();
},
),
),
),
大小盒子(
宽度:16.0,
),
扩大(
子项:TextFormField(
一旦更改:(值){
如果(value!=null&&value.length>0){
closeButtonVisibility.value=true;
}否则{
closeButtonVisibility.value=false;
}
},
onFieldSubmitted:(值){
(上下文)的焦点范围。取消焦点();
onSearchSubmit(值);
},
键盘类型:TextInputType.text,
textInputAction:textInputAction.search,
textcapitalize:textcapitalize.none,
textAlignVertical:textAlignVertical.center,
textAlign:textAlign.left,
maxLines:1,
控制器:textEditingController,
装饰:输入装饰(
是的,
边框:InputBorder.none,
hintText:“在此处搜索”,
),
),
),
ValueListenableBuilder(
valueListenable:closeButtonVisibility,
生成器:(上下文、值、子级){
返回值
?尺寸箱(
宽度:56.0,
 class SearchBar extends StatelessWidget {
  final TextEditingController textEditingController;
  final ValueNotifier<bool> closeButtonVisibility;
  final ValueChanged<String> onSearchSubmit;
  final VoidCallback onClose;

  const SearchBar({
    Key key,
    @required this.textEditingController,
    @required this.closeButtonVisibility,
    @required this.onSearchSubmit,
    @required this.onClose,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    return Container(
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 0),
        child: Row(
          children: <Widget>[
            SizedBox(
              height: 56.0,
              width: 56.0,
              child: Material(
                type: MaterialType.transparency,
                child: InkWell(
                  child: Icon(
                    Icons.arrow_back,
                    color: theme.textTheme.caption.color,
                  ),
                  onTap: () {
                    FocusScope.of(context).unfocus();
                    textEditingController.clear();
                    closeButtonVisibility.value = false;
                    onClose();
                  },
                ),
              ),
            ),
            SizedBox(
              width: 16.0,
            ),
            Expanded(
              child: TextFormField(
                onChanged: (value) {
                  if (value != null && value.length > 0) {
                    closeButtonVisibility.value = true;
                  } else {
                    closeButtonVisibility.value = false;
                  }
                },
                onFieldSubmitted: (value) {
                  FocusScope.of(context).unfocus();
                  onSearchSubmit(value);
                },
                keyboardType: TextInputType.text,
                textInputAction: TextInputAction.search,
                textCapitalization: TextCapitalization.none,
                textAlignVertical: TextAlignVertical.center,
                textAlign: TextAlign.left,
                maxLines: 1,
                controller: textEditingController,
                decoration: InputDecoration(
                  isDense: true,
                  border: InputBorder.none,
                  hintText: "Search here",
                ),
              ),
            ),
            ValueListenableBuilder<bool>(
                valueListenable: closeButtonVisibility,
                builder: (context, value, child) {
                  return value
                      ? SizedBox(
                          width: 56.0,
                          height: 56.0,
                          child: Material(
                            type: MaterialType.transparency,
                            child: InkWell(
                              child: Icon(
                                Icons.close,
                                color: theme.textTheme.caption.color,
                              ),
                              onTap: () {
                                closeButtonVisibility.value = false;
                                textEditingController.clear();
                              },
                            ),
                          ),
                        )
                      : Container();
                })
          ],
        ),
      ),
    );
  }
}