Flutter 实战Flutter

Flutter入门(35):Flutter 组件之 Bottom

2020-10-14  本文已影响0人  Maojunhao

1. 基本介绍

BottomSheet 是一种常见的上拉框,个人感觉 showModalBottomSheet 更为常用一点。

2. 示例代码

代码下载地址。如果对你有帮助的话记得给个关注,代码会根据我的 Flutter 专题不断更新。

3. 属性介绍

BottomSheet 属性 介绍
animationController 动画控制器
enableDrag 是否可以拖动,默认为 true
onDragStart 开始拖拽回调,没有找到具体使用场景,后续更新
onDragEnd 结束拖拽回调,没有找到具体使用场景,后续更新
backgroundColor 背景色
elevation 阴影高度
shape 形状 BorderShape
clipBehavior 剪切方式
onClosing 关闭回调函数
builder 构建函数

4. BottomSheet 详解

BottomSheet 作为组件直接使用的时候比较少,比如配合 Scaffold 的子属性使用,可以理解为展示在屏幕下方的一个组件。

import 'package:flutter/material.dart';

class FMBottomSheetVC extends StatefulWidget{
  @override
  FMBottomSheetState createState() => FMBottomSheetState();
}

class FMBottomSheetState extends State <FMBottomSheetVC>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return _scaffold(context);
  }

  Scaffold _scaffold(context){
    return Scaffold(
      appBar: AppBar(title: Text("BottomSheet"),),
      body: Center(
        // child: BottomSheetBtn(),
      ),
      bottomSheet: _bottomSheet(context),
      floatingActionButton: FloatingActionButton(
        child: Text("返回"),
        onPressed: (){
          Navigator.pop(context);
        },
      ),
    );
  }

  BottomSheet _bottomSheet(context){
    return BottomSheet(
      onClosing: (){
        print("closed");
      },

      builder: (context){
        return Container(
          height: 300,
          color: Colors.yellow,
          alignment: Alignment.centerLeft,
          child: Text("BottomSheet In Scaffold"),
        );
      },
    );
  }
}
BottomSheet.png

5. showModalBottomSheet 详解

showModalBottomSheet 是一个直接调起 BottomSheet 的 api,使用频率较高。

import 'package:flutter/material.dart';

class FMBottomSheetVC extends StatefulWidget{
  @override
  FMBottomSheetState createState() => FMBottomSheetState();
}

class FMBottomSheetState extends State <FMBottomSheetVC>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return _scaffold(context);
  }

  Scaffold _scaffold(context){
    return Scaffold(
      appBar: AppBar(title: Text("BottomSheet"),),
      body: Center(
        child: _raisedButton(context),
      ),
      // bottomSheet: _bottomSheet(context),
      // floatingActionButton: FloatingActionButton(
      //   child: Text("返回"),
      //   onPressed: (){
      //     Navigator.pop(context);
      //   },
      // ),
    );
  }

  BottomSheet _bottomSheet(context){
    return BottomSheet(
      onClosing: (){
        print("closed");
      },

      builder: (context){
        return Container(
          height: 300,
          color: Colors.yellow,
          alignment: Alignment.centerLeft,
          child: Text("BottomSheet In Scaffold"),
        );
      },
    );
  }

  RaisedButton _raisedButton(context){
    return RaisedButton(
      child: Text("showModalBottomSheet"),
      onPressed: (){
        showModalBottomSheet(
          context: context,
          builder: (context){
            return Container(
              width: 414,
              height: 300,
              color: Colors.red,
              alignment: Alignment.centerLeft,
              child: Text("showModalBottomSheet", style: TextStyle(color: Colors.white),),
            );
          },
        );
      },
    );
  }
}
showModalBottomSheet.gif

6. showBottomSheet 详解

showBottomSheet 对新手可能不太友好,它的实际调用是 Scaffold.of(context).showBottomSheet,.of(context) 方法在当前同一层级是拿不到 Scaffold Widget 的,所以会报错,需要在封装一层 class 进行使用。

6.1 常规报错

══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
No Scaffold widget found.
FMBottomSheetVC widgets require a Scaffold widget ancestor.
The specific widget that could not find a Scaffold ancestor was:
  FMBottomSheetVC
The ancestors of this widget were:
  ...
  Semantics
  Builder
  RepaintBoundary-[GlobalKey#26d4e]
  IgnorePointer
  AnimatedBuilder
  ...
Typically, the Scaffold widget is introduced by the MaterialApp or WidgetsApp widget at the top of
your application widget tree.

When the exception was thrown, this was the stack:
#0      debugCheckHasScaffold.<anonymous closure> (package:flutter/src/material/debug.dart:114:7)
#1      debugCheckHasScaffold (package:flutter/src/material/debug.dart:125:4)
#2      showBottomSheet (package:flutter/src/material/bottom_sheet.dart:725:10)
#3      FMBottomSheetState._raisedButton.<anonymous closure> (package:FMStudyApp/Widgets/Material_components/bottomsheet.dart:53:9)
#4      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
#5      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
#6      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
#7      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
#8      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:284:5)
#9      BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:219:7)
#10     PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:477:9)
#11     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:78:12)
#12     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:124:9)
#13     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
#14     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:122:18)
#15     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:108:7)
#16     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:220:19)
#17     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:200:22)
#18     GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:158:7)
#19     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:104:7)
#20     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:88:7)
#24     _invoke1 (dart:ui/hooks.dart:267:10)
#25     _dispatchPointerDataPacket (dart:ui/hooks.dart:176:5)
(elided 3 frames from dart:async)

Handler: "onTap"
Recognizer:
  TapGestureRecognizer#1116e
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by gesture ═══════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
No Scaffold widget found.

FMBottomSheetVC widgets require a Scaffold widget ancestor.
The specific widget that could not find a Scaffold ancestor was: FMBottomSheetVC
  dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#da548]]
  state: FMBottomSheetState#0bb6b
The ancestors of this widget were: 
  : MaterialApp
    state: _MaterialAppState#e550a
  : MyApp
  ...

Typically, the Scaffold widget is introduced by the MaterialApp or WidgetsApp widget at the top of your application widget tree.

When the exception was thrown, this was the stack: 
#0      debugCheckHasScaffold.<anonymous closure> (package:flutter/src/material/debug.dart:114:7)
#1      debugCheckHasScaffold (package:flutter/src/material/debug.dart:125:4)
#2      showBottomSheet (package:flutter/src/material/bottom_sheet.dart:725:10)
#3      FMBottomSheetState._raisedButton.<anonymous closure> (package:FMStudyApp/Widgets/Material_components/bottomsheet.dart:53:9)
#4      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#1116e
  debugOwner: GestureDetector
  state: possible
  won arena
  finalPosition: Offset(214.5, 497.0)
  finalLocalPosition: Offset(106.5, 17.0)
  button: 1
  sent tap down
════════════════════════════════════════════════════════════════════════════════════════════════════

No Scaffold widget found.
Typically, the Scaffold widget is introduced by the MaterialApp or WidgetsApp widget at the top of
your application widget tree.

6.2 解决方案

将 RaisedButton 单独用一个 class 封装一层即可。

import 'package:flutter/material.dart';

class FMBottomSheetVC extends StatefulWidget{
  @override
  FMBottomSheetState createState() => FMBottomSheetState();
}

class FMBottomSheetState extends State <FMBottomSheetVC>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    // return _materialApp(context);
    return _scaffold(context);
  }

  Scaffold _scaffold(context){
    return Scaffold(
      appBar: AppBar(title: Text("BottomSheet"),),
      body: Center(
        // child: _raisedButton(context),
        child: BottomSheetBtn(),
      ),
      // bottomSheet: _bottomSheet(context),
      // floatingActionButton: FloatingActionButton(
      //   child: Text("返回"),
      //   onPressed: (){
      //     Navigator.pop(context);
      //   },
      // ),
    );
  }

  BottomSheet _bottomSheet(context){
    return BottomSheet(
      onClosing: (){
        print("closed");
      },

      builder: (context){
        return Container(
          height: 300,
          color: Colors.yellow,
          alignment: Alignment.centerLeft,
          child: Text("BottomSheet In Scaffold"),
        );
      },
    );
  }

  RaisedButton _raisedButton(context){
    return RaisedButton(
      child: Text("showModalBottomSheet"),
      onPressed: (){
        showBottomSheet(
          context: context,
          builder: (context){
            return Container(
              width: 414,
              height: 300,
              color: Colors.red,
              alignment: Alignment.centerLeft,
              child: Text("showModalBottomSheet", style: TextStyle(color: Colors.white),),
            );
          },
        );
      },
    );
  }
}

class BottomSheetBtn extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return RaisedButton(
      child: Text("showBottomSheet"),
      onPressed: (){

        showBottomSheet(
          context: context,
          builder: (context){
            return Container(
              width: 414,
              height: 300,
              color: Colors.red,
              alignment: Alignment.centerLeft,
              child: Text("showBottomSheet", style: TextStyle(color: Colors.white),),
            );
          },
        );
      },
    );
  }
}

可以用以下方法收起上拉框

Navigator.pop(context);

showBottomSheet.gif

7. 技术小结

上拉框是一个很常用的控件,其实除了唤起,其他属性和该组件并没有太大关系,考验的还是各种组件搭配使用的基本功。

上一篇下一篇

猜你喜欢

热点阅读