Flutter底部弹出视图插件snapping_sheet_nic的使用

Flutter底部弹出视图插件snapping_sheet_nic的使用

Snapping Sheet

example workflow pub points popularity likes License: MIT

一个提供了高度可定制的底部弹出视图组件的包,可以吸附到不同的垂直和水平位置。

特点

  • 可以适应底部弹出视图内部的滚动控件。
  • 完全自定义动画和内容。
  • 不限于应用底部部分。

你可以找到并运行例子,关闭此仓库并从example文件夹运行应用。

开始使用

首先,在你的pubspec.yaml文件中添加该包。以下是基本的使用示例:

import 'package:flutter/material.dart';
import 'package:snapping_sheet/snapping_sheet.dart';

class GettingStartedExample extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: SnappingSheet(
        // 背景内容(可留空)
        child: MyOwnPageContent(), 
        grabbingHeight: 75,
        // 拖拽区域
        grabbing: MyOwnGrabbingWidget(),
        sheetBelow: SnappingSheetContent(
          draggable: true,
          // 弹出视图内容
          child: MyOwnSheetContent(),
        ),
      ),
    );
  }
}

自定义吸附位置

通过修改snappingPositions参数来改变吸附位置,参数可以接受SnappingPosition.factorSnappingPosition.pixels对象。这些对象用于指定位置使用因子或像素值。你还可以指定吸附的速度和动画曲线。

SnappingSheet(
  snappingPositions: [
    SnappingPosition.factor(
      positionFactor: 0.0,
      snappingCurve: Curves.easeOutExpo,
      snappingDuration: Duration(seconds: 1),
      grabbingContentOffset: GrabbingContentOffset.top,
    ),
    SnappingPosition.pixels(
      positionPixels: 400,
      snappingCurve: Curves.elasticOut,
      snappingDuration: Duration(milliseconds: 1750),
    ),
    SnappingPosition.factor(
      positionFactor: 1.0,
      snappingCurve: Curves.bounceOut,
      snappingDuration: Duration(seconds: 1),
      grabbingContentOffset: GrabbingContentOffset.bottom,
    ),
  ],
)

添加内容到弹出视图

你可以在吸附区域的上方或下方添加内容。如果不需要在某个区域添加内容,可以传入null。

  • sizeBehavior: 控制内容大小的行为,可以是SheetSizeFill(填充可用空间)或SheetSizeStatic(固定高度)。
  • draggable: 是否允许拖动展开或关闭弹出视图。
  • child: 任何你需要的Widget。
SnappingSheet(
  sheetAbove: SnappingSheetContent(
    sizeBehavior: SheetSizeFill(),
    draggable: false,
    child: Container(color: Colors.blue),
  ),
  sheetBelow: SnappingSheetContent(
    sizeBehavior: SheetSizeStatic(size: 300),
    draggable: true,
    child: Container(color: Colors.red),
  ),
)

使弹出视图适应滚动控制器

为了让弹出视图知道滚动控制器的存在,需要在SnappingSheetContent类中传递它。建议将lockOverflowDrag设置为true,以防止视图超出最大最小吸附位置。

SnappingSheet(
  lockOverflowDrag: true, // 建议设置为true
  sheetBelow: SnappingSheetContent(
    // 传递滚动控制器
    childScrollController: _myScrollController,
    draggable: true,
    child: ListView(
      // 在滚动控件中使用相同的控制器
      controller: _myScrollController,

      // 注意:如果在sheetBelow中,应设置为false;如果在sheetAbove中,应设置为true
      reverse: false,
    ),
  ),
)

注意:滚动控件如ListViewSingleChildScrollView等需要根据其位置正确设置reverse属性。如果在sheetBelow中,reverse应设为false;如果在sheetAbove中,应设为true。

使用SnappingSheetController

可以通过SnappingSheetController控制弹出视图。

// 创建控制器
final snappingSheetController = SnappingSheetController();
SnappingSheet(
  controller: snappingSheetController,
  sheetAbove: SnappingSheetContent(
    draggable: false,
    child: Container(color: Colors.blue),
  ),
  sheetBelow: SnappingSheetContent(
    draggable: true,
    child: Container(color: Colors.red),
  ),
)

// 控制弹出视图
snappingSheetController.snapToPosition(
  SnappingPosition.factor(positionFactor: 0.75),
);
snappingSheetController.stopCurrentSnapping();
snappingSheetController.setSnappingSheetPosition(100);

// 获取弹出视图信息
snappingSheetController.currentPosition;
snappingSheetController.currentSnappingPosition;
snappingSheetController.currentlySnapping;
snappingSheetController.isAttached;

监听变化

你可以监听移动和吸附动画完成事件:

SnappingSheet(
  onSheetMoved: (sheetPosition) {
    print("当前位置 ${sheetPosition.pixels}");
  },
  onSnapCompleted: (sheetPosition, snappingPosition) {
    print("当前位置 ${sheetPosition.pixels}");
    print("当前吸附位置 $snappingPosition");
  },
  onSnapStart: (sheetPosition, snappingPosition) {
    print("当前位置 ${sheetPosition.pixels}");
    print("下一个吸附位置 $snappingPosition");
  },
)

水平吸附视图

还存在水平模式的吸附视图。要使用它,请参阅以下代码:

SnappingSheet.horizontal(
  // 背景内容(可留空)
  child: MyOwnPageContent(), 
  grabbingWidth: 50,
  // 拖拽区域
  grabbing: MyOwnGrabbingWidget(),
  sheetLeft: SnappingSheetContent(
    draggable: true,
    // 弹出视图内容
    child: MyOwnSheetContent(),
  ),
),

更复杂的水平吸附视图示例可以在这里找到。

完整示例

以下是一个完整的示例,展示了如何使用snapping_sheet_nic插件创建一个带有列表的底部弹出视图。

import 'package:example/pages/menu.dart';
import 'package:flutter/material.dart';
import 'package:snapping_sheet/snapping_sheet.dart';

void main() {
  runApp(SnappingSheetExampleApp());
}

class SimpleSnappingSheet extends StatelessWidget {
  final ScrollController listViewController = new ScrollController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SnappingSheet(
      child: Background(),
      lockOverflowDrag: true,
      snappingPositions: [
        SnappingPosition.factor(
          positionFactor: 0.0,
          snappingCurve: Curves.easeOutExpo,
          snappingDuration: Duration(seconds: 1),
          grabbingContentOffset: GrabbingContentOffset.top,
        ),
        SnappingPosition.factor(
          snappingCurve: Curves.elasticOut,
          snappingDuration: Duration(milliseconds: 1750),
          positionFactor: 0.5,
        ),
        SnappingPosition.factor(
          grabbingContentOffset: GrabbingContentOffset.bottom,
          snappingCurve: Curves.easeInExpo,
          snappingDuration: Duration(seconds: 1),
          positionFactor: 0.9,
        ),
      ],
      grabbing: GrabbingWidget(),
      grabbingHeight: 75,
      sheetAbove: null,
      sheetBelow: SnappingSheetContent(
        draggable: true,
        childScrollController: listViewController,
        child: Container(
          color: Colors.white,
          child: ListView.builder(
            controller: listViewController,
            itemBuilder: (context, index) {
              return Container(
                margin: EdgeInsets.all(15),
                color: Colors.green[200],
                height: 100,
                child: Center(
                  child: Text(index.toString()),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

/// 辅助组件

class Background extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey[200],
      child: Placeholder(
        color: Colors.green[200]!,
      ),
    );
  }
}

class GrabbingWidget extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
        boxShadow: [
          BoxShadow(blurRadius: 25, color: Colors.black.withOpacity(0.2)),
        ],
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Container(
            margin: EdgeInsets.only(top: 20),
            width: 100,
            height: 7,
            decoration: BoxDecoration(
              color: Colors.grey,
              borderRadius: BorderRadius.circular(5),
            ),
          ),
          Container(
            color: Colors.grey[200],
            height: 2,
            margin: EdgeInsets.all(15).copyWith(top: 0, bottom: 0),
          )
        ],
      ),
    );
  }
}

class SnappingSheetExampleApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Snapping Sheet Examples',
      theme: ThemeData(
        appBarTheme: AppBarTheme(
          backgroundColor: Colors.grey[700],
          elevation: 0,
          foregroundColor: Colors.white,
          titleTextStyle: TextStyle(
            color: Colors.white,
            fontWeight: FontWeight.bold,
          ),
          iconTheme: IconThemeData(color: Colors.white),
        ),
        primarySwatch: Colors.grey,
      ),
      home: PageWrapper(),
    );
  }
}

class PageWrapper extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          "Example",
          style: TextStyle(color: Colors.white),
        ),
        actions: [
          IconButton(
            icon: Icon(Icons.menu),
            onPressed: () => {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) {
                  return Menu();
                }),
              ),
            },
          )
        ],
      ),
      body: SimpleSnappingSheet(),
    );
  }
}

更多关于Flutter底部弹出视图插件snapping_sheet_nic的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter底部弹出视图插件snapping_sheet_nic的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


snapping_sheet_nic 是一个用于 Flutter 的插件,它允许你创建一个底部弹出的视图,并且可以自定义其行为和样式。这个插件特别适合用于实现类似 Google Maps 或 Apple Maps 中的底部可拖动面板。

安装

首先,你需要在 pubspec.yaml 文件中添加 snapping_sheet_nic 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  snapping_sheet_nic: ^1.0.0 # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

基本用法

以下是一个简单的例子,展示了如何使用 snapping_sheet_nic 插件创建一个底部弹出的视图:

import 'package:flutter/material.dart';
import 'package:snapping_sheet_nic/snapping_sheet_nic.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Snapping Sheet Demo'),
        ),
        body: SnappingSheetDemo(),
      ),
    );
  }
}

class SnappingSheetDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SnappingSheet(
      child: Center(
        child: Text('Main Content'),
      ),
      grabbingHeight: 75,
      grabbing: Container(
        color: Colors.blue,
        child: Center(
          child: Text('Grabbing Area'),
        ),
      ),
      sheetAbove: SnappingSheetContent(
        child: Container(
          color: Colors.white,
          child: Center(
            child: Text('Sheet Above Content'),
          ),
        ),
      ),
      sheetBelow: SnappingSheetContent(
        child: Container(
          color: Colors.green,
          child: Center(
            child: Text('Sheet Below Content'),
          ),
        ),
      ),
      snappingPositions: [
        SnappingPosition.factor(
          positionFactor: 0.0,
          grabbingContentOffset: GrabbingContentOffset.top,
        ),
        SnappingPosition.factor(
          positionFactor: 0.5,
          grabbingContentOffset: GrabbingContentOffset.top,
        ),
        SnappingPosition.factor(
          positionFactor: 1.0,
          grabbingContentOffset: GrabbingContentOffset.top,
        ),
      ],
    );
  }
}

参数解释

  • child: 主内容区域,通常是一个 Widget,例如 CenterColumn 等。
  • grabbingHeight: 抓取区域的高度,用户可以通过拖动这个区域来控制面板的展开和收起。
  • grabbing: 抓取区域的 Widget,通常是一个容器或按钮。
  • sheetAbove: 面板上面的内容,通常是一个 SnappingSheetContent
  • sheetBelow: 面板下面的内容,通常是一个 SnappingSheetContent
  • snappingPositions: 面板的吸附位置,你可以通过 SnappingPosition.factor 来定义不同的吸附位置。

自定义

你可以通过调整 snappingPositions 来定义面板的吸附位置。例如,你可以设置面板在 0%、50% 和 100% 的位置吸附。

snappingPositions: [
  SnappingPosition.factor(
    positionFactor: 0.0,
    grabbingContentOffset: GrabbingContentOffset.top,
  ),
  SnappingPosition.factor(
    positionFactor: 0.5,
    grabbingContentOffset: GrabbingContentOffset.top,
  ),
  SnappingPosition.factor(
    positionFactor: 1.0,
    grabbingContentOffset: GrabbingContentOffset.top,
  ),
],
回到顶部