zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    对象池使用时要注意几点
    Flash3D学习计划(一)——3D渲染的一般管线流程
    714. Best Time to Buy and Sell Stock with Transaction Fee
    712. Minimum ASCII Delete Sum for Two Strings
    647. Palindromic Substrings(马拉车算法)
    413. Arithmetic Slices
    877. Stone Game
    338. Counting Bits
    303. Range Sum Query
    198. House Robber
  • 原文地址:https://www.cnblogs.com/sundaysme/p/12703412.html
Copyright © 2011-2022 走看看