grabber_sheet 1.2.1
grabber_sheet: ^1.2.1 copied to clipboard
Stop fighting gesture conflicts. A production-ready bottom sheet with a persistent grabber that just works.
import 'package:flutter/material.dart';
import 'package:grabber_sheet/grabber_sheet.dart';
void main() => runApp(const DraggableSheetExampleApp());
class DraggableSheetExampleApp extends StatelessWidget {
const DraggableSheetExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.lightBlue,
surface: Colors.lightBlue.shade50,
onSurface: Colors.black87,
),
),
home: const ExampleHomePage(),
);
}
}
class ExampleHomePage extends StatefulWidget {
const ExampleHomePage({super.key});
@override
State<ExampleHomePage> createState() => _ExampleHomePageState();
}
class _ExampleHomePageState extends State<ExampleHomePage> {
final GrabberSheetController _grabberSheetController =
GrabberSheetController();
String _currentSheetStatus = 'Idle';
double _currentSize = 0.5;
@override
void initState() {
super.initState();
_grabberSheetController.addListener(() {
if (_grabberSheetController.isAttached && mounted) {
setState(() {
_currentSize = _grabberSheetController.size;
});
}
});
}
@override
void dispose() {
_grabberSheetController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final sheetColor = Colors.blue.shade100;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('GrabberSheet Example'),
backgroundColor: sheetColor,
),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton.small(
heroTag: 'maximize',
onPressed: () => _grabberSheetController.maximize(),
tooltip: 'Maximize',
child: const Icon(Icons.keyboard_arrow_up),
),
const SizedBox(height: 8),
FloatingActionButton.small(
heroTag: 'minimize',
onPressed: () => _grabberSheetController.minimize(),
tooltip: 'Minimize',
child: const Icon(Icons.keyboard_arrow_down),
),
const SizedBox(height: 8),
FloatingActionButton.small(
heroTag: 'animate',
onPressed: () => _grabberSheetController.animateTo(
0.7,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
),
tooltip: 'Animate to 0.7',
child: const Icon(Icons.height),
),
],
),
body: Stack(
children: [
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Background Content',
style: theme.textTheme.headlineMedium,
),
const SizedBox(height: 20),
Text('Sheet Size: ${_currentSize.toStringAsFixed(2)}'),
Text('Sheet Status: $_currentSheetStatus'),
],
),
),
GrabberSheet(
controller: _grabberSheetController,
initialChildSize: 0.5,
minChildSize: 0.2,
maxChildSize: 0.8,
snap: true,
snapSizes: const [.5],
backgroundColor: sheetColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
grabberStyle: GrabberStyle(color: Colors.grey.shade400),
bottom: Row(
children: [
const Text('sheet title'),
const Spacer(),
IconButton(onPressed: () {}, icon: const Icon(Icons.close)),
],
),
bottomAreaPadding: const EdgeInsets.symmetric(horizontal: 16),
onSizeChanged: (size) {
if (mounted) {
setState(() {
_currentSheetStatus = 'Dragging/Resizing';
_currentSize = size;
});
}
},
onSnap: (size) {
if (mounted) {
setState(() {
_currentSheetStatus = 'Snapped to ${size.toStringAsFixed(2)}';
_currentSize = size;
});
}
},
builder: (BuildContext context, ScrollController scrollController) {
return ListView.builder(
controller: scrollController,
itemCount: 30,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(
'Item $index',
style: TextStyle(color: theme.colorScheme.onSurface),
),
);
},
);
},
),
],
),
);
}
}