Flutter入门(35):Flutter 组件之 BottomSheet 详解

1. 基本介绍

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

2. 示例代码

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

3. 属性介绍

BottomSheet 属性

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"),

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),),

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