首页 > 软件编程 > Android > flutter的showModalBottomSheet的坑

使用flutter的showModalBottomSheet遇到的坑及解决

作者:沙漠一只雕得儿得儿

这篇文章主要介绍了使用flutter的showModalBottomSheet遇到的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

在使用官方的showModalBottomSheet这个组件时到目前为止

遇到了三个比较坑的地方

1. 无法直接设置圆角;

2. 组件最多只能撑满半屏幕,再多就出界了;

3. 在这个组件里面如果有选择按钮等其他一些需要改变状态的组件时,即便使用setState,状态也无法更新。

我们解决完后的效果如下

解决问题一

使用stack包裹住子组件解决圆角的问题,且需要设置背景颜色为Colors.balck54,这个颜色是bottomsheet弹出时系统的默认颜色,最简单的示例代码如下:

                 showModalBottomSheet(
                    context: context,
                    builder: (BuildContext bc) {
                      return Stack(
                        children: <Widget>[
                          Container(
                            height: 30.0,
                            width: double.infinity,
                            color: Colors.black54,
                          Container(
                            decoration: BoxDecoration(
                                color: Colors.white,
                                borderRadius: BorderRadius.only(
                                  topLeft: Radius.circular(25),
                                  topRight: Radius.circular(25),
                          Container(
                            child: FlatButton(
                              child: Container(
                                alignment: Alignment.center,
                                padding:
                                    EdgeInsets.only(top: 33.0, bottom: 33.0),
                                child: Text(
                                  "bottomSheet的内容",

圆角有了,且圆角颜色和背景色都是black54,效果如图:

解决问题二

系统的bottomSheet最大高度是屏幕的一半,原因是源码里面限制了最大高度:

maxHeight: constraints.maxHeight * 9.0 / 16.0,

我们解决办法是直接把源码文件考出来,把这个值给去掉即可。拷贝源码唯一需要注意的一点是import导包时,源码的import 路径和我们自己导的路径不同,

源码的import:                                                     我们导入的import:

嫌麻烦的话,文末有已经修改好的可以直接使用的bottomSheet文件。只是修改了maxHeight这个限制属性。这个去掉后,bottomSheet就没有最大高度限制了。

解决问题三

在bottomSheet里面如果有需要更改状态的组件,例如CheckBox的选中、未选中状态,这时setState(){}发现bottomSheet本身没有更新。

这边想到的方法是使用evenbus,在bottomSheet里面需要更新的地方发射更新信息,在拷贝出的系统源码中加入listen即可,如下:

@override
  void initState() {
    super.initState();
    Manager.instance.eventBus.on<RefreshBottomSheetEvent>().listen((event) {
      setState(() {

fire消息的代码:

Manager.instance.eventBus.fire(RefreshBottomSheetEvent());

这个event:

class RefreshBottomSheetEvent {
  RefreshBottomSheetEvent();

下面这个即为整个修改源码的bottomSheet,改动的地方:

1. maxHeight

2.添加了eventBus的listen

// Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:phone_assistant/event/ContactRefreshEvent.dart'; import 'package:phone_assistant/event/RefreshBottomSheetEvent.dart'; import '../../Manager.dart'; const Duration _kBottomSheetDuration = Duration(milliseconds: 200); const double _kMinFlingVelocity = 700.0; const double _kCloseProgressThreshold = 0.5; /// A material design bottom sheet. /// There are two kinds of bottom sheets in material design: /// * _Persistent_. A persistent bottom sheet shows information that /// supplements the primary content of the app. A persistent bottom sheet /// remains visible even when the user interacts with other parts of the app. /// Persistent bottom sheets can be created and displayed with the /// [ScaffoldState.showBottomSheet] function or by specifying the /// [Scaffold.bottomSheet] constructor parameter. /// * _Modal_. A modal bottom sheet is an alternative to a menu or a dialog and /// prevents the user from interacting with the rest of the app. Modal bottom /// sheets can be created and displayed with the [showModalBottomSheet] /// function. /// The [BottomSheet] widget itself is rarely used directly. Instead, prefer to /// create a persistent bottom sheet with [ScaffoldState.showBottomSheet] or /// [Scaffold.bottomSheet], and a modal bottom sheet with [showModalBottomSheet]. /// See also: /// * [showBottomSheet] and [ScaffoldState.showBottomSheet], for showing /// non-modal "persistent" bottom sheets. /// * [showModalBottomSheet], which can be used to display a modal bottom /// sheet. /// * <https://material.io/design/components/sheets-bottom.html> class BottomSheet extends StatefulWidget { /// Creates a bottom sheet. /// Typically, bottom sheets are created implicitly by /// [ScaffoldState.showBottomSheet], for persistent bottom sheets, or by /// [showModalBottomSheet], for modal bottom sheets. const BottomSheet({ Key key, this.animationController, this.enableDrag = true, this.elevation = 0.0, @required this.onClosing, @required this.builder, }) : assert(enableDrag != null), assert(onClosing != null), assert(builder != null), assert(elevation != null && elevation >= 0.0), super(key: key); /// The animation that controls the bottom sheet's position. /// The BottomSheet widget will manipulate the position of this animation, it /// is not just a passive observer. final AnimationController animationController; /// Called when the bottom sheet begins to close. /// A bottom sheet might be prevented from closing (e.g., by user /// interaction) even after this callback is called. For this reason, this /// callback might be call multiple times for a given bottom sheet. final VoidCallback onClosing; /// A builder for the contents of the sheet. /// The bottom sheet will wrap the widget produced by this builder in a /// [Material] widget. final WidgetBuilder builder; /// If true, the bottom sheet can dragged up and down and dismissed by swiping /// downwards. /// Default is true. final bool enableDrag; /// The z-coordinate at which to place this material relative to its parent. /// This controls the size of the shadow below the material. /// Defaults to 0. The value is non-negative. final double elevation; @override _BottomSheetState createState() => _BottomSheetState(); /// Creates an animation controller suitable for controlling a [BottomSheet]. static AnimationController createAnimationController(TickerProvider vsync) { return AnimationController( duration: _kBottomSheetDuration, debugLabel: 'BottomSheet', vsync: vsync, class _BottomSheetState extends State<BottomSheet> { @override void initState() { super.initState(); Manager.instance.eventBus.on<RefreshBottomSheetEvent>().listen((event) { setState(() { final GlobalKey _childKey = GlobalKey(debugLabel: 'BottomSheet child'); double get _childHeight { final RenderBox renderBox = _childKey.currentContext.findRenderObject(); return renderBox.size.height; bool get _dismissUnderway => widget.animationController.status == AnimationStatus.reverse; void _handleDragUpdate(DragUpdateDetails details) { if (_dismissUnderway) return; widget.animationController.value -= details.primaryDelta / (_childHeight ?? details.primaryDelta); void _handleDragEnd(DragEndDetails details) { if (_dismissUnderway) return; if (details.velocity.pixelsPerSecond.dy > _kMinFlingVelocity) { final double flingVelocity = -details.velocity.pixelsPerSecond.dy / _childHeight; if (widget.animationController.value > 0.0) widget.animationController.fling(velocity: flingVelocity); if (flingVelocity < 0.0) widget.onClosing(); } else if (widget.animationController.value < _kCloseProgressThreshold) { if (widget.animationController.value > 0.0) widget.animationController.fling(velocity: -1.0); widget.onClosing(); } else { widget.animationController.forward(); @override Widget build(BuildContext context) { final Widget bottomSheet = Material( key: _childKey, elevation: widget.elevation, child: widget.builder(context), return !widget.enableDrag ? bottomSheet : GestureDetector( onVerticalDragUpdate: _handleDragUpdate, onVerticalDragEnd: _handleDragEnd, child: bottomSheet, excludeFromSemantics: true, // PERSISTENT BOTTOM SHEETS // See scaffold.dart // MODAL BOTTOM SHEETS class _ModalBottomSheetLayout extends SingleChildLayoutDelegate { _ModalBottomSheetLayout(this.progress); final double progress; @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { return BoxConstraints( minWidth: constraints.maxWidth, maxWidth: constraints.maxWidth, minHeight: 0.0, // maxHeight: constraints.maxHeight * 9.0 / 16.0, @override Offset getPositionForChild(Size size, Size childSize) { return Offset(0.0, size.height - childSize.height * progress); @override bool shouldRelayout(_ModalBottomSheetLayout oldDelegate) { return progress != oldDelegate.progress; class _ModalBottomSheet<T> extends StatefulWidget { const _ModalBottomSheet({ Key key, this.route }) : super(key: key); final _ModalBottomSheetRoute<T> route; @override _ModalBottomSheetState<T> createState() => _ModalBottomSheetState<T>(); class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> { @override Widget build(BuildContext context) { final MediaQueryData mediaQuery = MediaQuery.of(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); String routeLabel; switch (defaultTargetPlatform) { case TargetPlatform.iOS: routeLabel = ''; break; case TargetPlatform.android: case TargetPlatform.fuchsia: routeLabel = localizations.dialogLabel; break; return GestureDetector( excludeFromSemantics: true, onTap: () => Navigator.pop(context), child: AnimatedBuilder( animation: widget.route.animation, builder: (BuildContext context, Widget child) { // Disable the initial animation when accessible navigation is on so // that the semantics are added to the tree at the correct time. final double animationValue = mediaQuery.accessibleNavigation ? 1.0 : widget.route.animation.value; return Semantics( scopesRoute: true, namesRoute: true, label: routeLabel, explicitChildNodes: true, child: ClipRect( child: CustomSingleChildLayout( delegate: _ModalBottomSheetLayout(animationValue), child: BottomSheet( animationController: widget.route._animationController, onClosing: () => Navigator.pop(context), builder: widget.route.builder, class _ModalBottomSheetRoute<T> extends PopupRoute<T> { _ModalBottomSheetRoute({ this.builder, this.theme, this.barrierLabel, RouteSettings settings, }) : super(settings: settings); final WidgetBuilder builder; final ThemeData theme; @override Duration get transitionDuration => _kBottomSheetDuration; @override bool get barrierDismissible => true; @override final String barrierLabel; @override Color get barrierColor => Colors.black54; AnimationController _animationController; @override AnimationController createAnimationController() { assert(_animationController == null); _animationController = BottomSheet.createAnimationController(navigator.overlay); return _animationController; @override Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { // By definition, the bottom sheet is aligned to the bottom of the page // and isn't exposed to the top padding of the MediaQuery. Widget bottomSheet = MediaQuery.removePadding( context: context, removeTop: true, child: _ModalBottomSheet<T>(route: this), if (theme != null) bottomSheet = Theme(data: theme, child: bottomSheet); return bottomSheet; /// Shows a modal material design bottom sheet. /// A modal bottom sheet is an alternative to a menu or a dialog and prevents /// the user from interacting with the rest of the app. /// A closely related widget is a persistent bottom sheet, which shows /// information that supplements the primary content of the app without /// preventing the use from interacting with the app. Persistent bottom sheets /// can be created and displayed with the [showBottomSheet] function or the /// [ScaffoldState.showBottomSheet] method. /// The `context` argument is used to look up the [Navigator] and [Theme] for /// the bottom sheet. It is only used when the method is called. Its /// corresponding widget can be safely removed from the tree before the bottom /// sheet is closed. /// Returns a `Future` that resolves to the value (if any) that was passed to /// [Navigator.pop] when the modal bottom sheet was closed. /// See also: /// * [BottomSheet], which is the widget normally returned by the function /// passed as the `builder` argument to [showModalBottomSheet]. /// * [showBottomSheet] and [ScaffoldState.showBottomSheet], for showing /// non-modal bottom sheets. /// * <https://material.io/design/components/sheets-bottom.html#modal-bottom-sheet> Future<T> showModalBottomSheetCustom<T>({ @required BuildContext context, @required WidgetBuilder builder, assert(context != null); assert(builder != null); assert(debugCheckHasMaterialLocalizations(context)); return Navigator.push(context, _ModalBottomSheetRoute<T>( builder: builder, theme: Theme.of(context, shadowThemeOnly: true), barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, /// Shows a persistent material design bottom sheet in the nearest [Scaffold]. /// Returns a controller that can be used to close and otherwise manipulate the /// bottom sheet. /// To rebuild the bottom sheet (e.g. if it is stateful), call /// [PersistentBottomSheetController.setState] on the controller returned by /// this method. /// The new bottom sheet becomes a [LocalHistoryEntry] for the enclosing /// [ModalRoute] and a back button is added to the appbar of the [Scaffold] /// that closes the bottom sheet. /// To create a persistent bottom sheet that is not a [LocalHistoryEntry] and /// does not add a back button to the enclosing Scaffold's appbar, use the /// [Scaffold.bottomSheet] constructor parameter. /// A persistent bottom sheet shows information that supplements the primary /// content of the app. A persistent bottom sheet remains visible even when /// the user interacts with other parts of the app. /// A closely related widget is a modal bottom sheet, which is an alternative /// to a menu or a dialog and prevents the user from interacting with the rest /// of the app. Modal bottom sheets can be created and displayed with the /// [showModalBottomSheet] function. /// The `context` argument is used to look up the [Scaffold] for the bottom /// sheet. It is only used when the method is called. Its corresponding widget /// can be safely removed from the tree before the bottom sheet is closed. /// See also: /// * [BottomSheet], which is the widget typically returned by the `builder`. /// * [showModalBottomSheet], which can be used to display a modal bottom /// sheet. /// * [Scaffold.of], for information about how to obtain the [BuildContext]. /// * <https://material.io/design/components/sheets-bottom.html#standard-bottom-sheet> PersistentBottomSheetController<T> showBottomSheet<T>({ @required BuildContext context, @required WidgetBuilder builder, assert(context != null); assert(builder != null); return Scaffold.of(context).showBottomSheet<T>(builder);

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
  • Flutter开发setState能否在build中直接调用详解
    Flutter开发setState能否在build中直接调用详解
    2022-10-10
  • Jetpack Compose常用组件详细介绍
    Jetpack Compose常用组件详细介绍
    2022-10-10
  • Kotlin HttpURLConnection与服务器交互实现方法详解
    Kotlin HttpURLConnection与服务器交互实现方法详解
    2022-09-09
  • Kotlin 协程的取消机制详细解读
    Kotlin 协程的取消机制详细解读
    2022-10-10
  • FFmpeg Principle分析Out put File 数据结构
    FFmpeg Principle分析Out put File 数据结
    2022-10-10
  • FFmpeg Principle学习open_output_file打开输出文件
    FFmpeg Principle学习open_output_fil
    2022-10-10
  • FFmpeg Principle学习new_video_stream添加视频输出流
    FFmpeg Principle学习new_video_stream添加视
    2022-10-10
  • 设置界面开发Preference Library数据重建机制详解
    设置界面开发Preference Library数据重建机制详解
    2022-10-10
  • 美国设下计谋,用娘炮文化重塑日本,已影响至中国
    美国设下计谋,用娘炮文化重塑日本,已影响至中国
    2021-11-19
  • 时空伴随者是什么意思?时空伴随者介绍
    时空伴随者是什么意思?时空伴随者介绍
    2021-11-09
  • 工信部称网盘企业免费用户最低速率应满足基本下载需求,天翼云盘回应:坚决支持,始终
    工信部称网盘企业免费用户最低速率应满足基本下载需求,天翼云盘回应:坚决支持,始终
    2021-11-05
  • 2022年放假安排出炉:五一连休5天 2022年所有节日一览表
    2022年放假安排出炉:五一连休5天 2022年所有节日一览表
    2021-10-26
  • 电脑版 - 返回首页

    2006-2023 脚本之家 JB51.Net , All Rights Reserved.
    苏ICP备14036222号