• flutter的showModalBottomSheet遇到的坑


    https://blog.csdn.net/cpcpcp123/article/details/97660036

    在使用官方的showModalBottomSheet这个组件时到目前为止遇到了三个比较坑的地方:

    1. 无法直接设置圆角;

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

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

    我们解决完后的效果如下,

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

                     showModalBottomSheet(

                        context: context,

                        builder: (BuildContext bc) {

                          return Stack(

                            children: <Widget>[

                              Container(

                                height: 30.0,

                                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

    // Copyright 2015 The Chromium Authors. All rights reserved.

    // 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);

    }

    ————————————————

    版权声明:本文为CSDN博主「buder得儿得儿以得儿以得儿得儿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

    原文链接:https://blog.csdn.net/cpcpcp123/java/article/details/97660036

  • 相关阅读:
    【design pattern】代理模式
    Java IO(十七)FIleReader 和 FileWriter
    Java IO(十六)InputStreamReader 和 InputStreamWriter
    Java IO(十五)FilterReader 和 FilterWriter、FilterReader 子类 PushBackReader
    Java IO(十四) CharArrayReader 和 CharArrayWriter
    Java IO(十三)PipedReader 和 PipedWriter
    Java IO(十二) 字符流 Writer 和 Reader
    Java IO(十一) DataInputStream 和 DataOutputStream
    Java IO(十) BufferedInputStream 和 BufferedOutputStream
    Java IO(九)FilterInputStream 和 FilterOutputStream
  • 原文地址:https://www.cnblogs.com/sundaysme/p/12703412.html
Copyright © 2020-2023  润新知