sliding_up_panel 0.3.6
sliding_up_panel #
A draggable Flutter widget that makes implementing a SlidingUpPanel much easier! Based on the Material Design bottom sheet component, this widget works on both Android & iOS.
Installing #
Add the following to your pubspec.yaml
file:
dependencies:
sliding_up_panel: ^0.3.6
Simple Usage #
There are two ways which the SlidingUpPanel
can easily be added to your project.
- Using the
SlidingUpPanel
as the root widget for the body (recommended). - Nesting the
SlidingUpPanel
SlidingUpPanel
as the Root (recommended)
This method is recommended as it allows for the least interference with the behavior of other UI elements. For example:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
panel: Center(
child: Text("This is the sliding Widget"),
),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
),
);
}
Nesting the SlidingUpPanel
This method isn't recommended but can still be used. Only use this to avoid refactoring large chunks of code or to implement custom scrolling behavior. For example, the SlidingUpPanel
can be nested inside of a Stack
(note that there are many other possible implementations that vary on a case-by-case basis).
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: Stack(
children: <Widget>[
Center(child: Text("This is the Widget behind the sliding panel"),),
SlidingUpPanel(
panel: Center(child: Text("This is the sliding Widget"),),
)
],
)
);
}
Screenshots #
Both methods produce the same result:
Custom Usage #
There are several options that allow for more control:
Properties | Description |
---|---|
panel | (required) The Widget that slides into view. When the panel is collapsed and if collapsed is null, then top portion of this Widget will be displayed; otherwise, collapsed will be displayed overtop of this Widget. |
collapsed | The Widget displayed overtop the panel when collapsed. This fades out as the panel is opened. |
body | The Widget that lies underneath the sliding panel. This Widget automatically sizes itself to fill the screen. |
minHeight | The height of the sliding panel when fully collapsed. |
maxHeight | The height of the sliding panel when fully open. |
border | A border to draw around the sliding panel sheet. |
borderRadius | If non-null, the corners of the sliding panel sheet are rounded by this. |
boxShadow | A list of shadows cast behind the sliding panel sheet. |
color | The color to fill the background of the sliding panel sheet. |
padding | The amount to inset the children of the sliding panel sheet. |
margin | Empty space surrounding the sliding panel sheet. |
renderPanelSheet | Set to false to not to render the sheet the panel sits upon. This means that only body , collapsed , and the panel Widgets will be rendered. Set this to false if you want to achieve a floating effect or want more customization over how the sliding panel looks like. |
panelSnapping | Set to false to disable the panel from snapping open or closed. |
backdropEnabled | If non-null, shows a darkening shadow over the body as the panel slides open. |
backdropColor | Shows a darkening shadow of this Color over the body as the panel slides open. |
backdropOpacity | The opacity of the backdrop when the panel is fully open. This value can range from 0.0 to 1.0 where 0.0 is completely transparent and 1.0 is completely opaque. |
backdropTapClosesPanel | Flag that indicates whether or not tapping the backdrop closes the panel. Defaults to true. |
controller | If non-null, this can be used to control the state of the panel. |
onPanelSlide | If non-null, this callback is called as the panel slides around with the current position of the panel. The position is a double between 0.0 and 1.0 where 0.0 is fully collapsed and 1.0 is fully open. |
onPanelOpened | If non-null, this callback is called when the panel is fully opened. |
onPanelClosed | If non-null, this callback is called when the panel is fully collapsed. |
parallaxEnabled | If non-null and true, the SlidingUpPanel exhibits a parallax effect as the panel slides up. Essentially, the body slides up as the panel slides up. |
parallaxOffset | Allows for specifying the extent of the parallax effect in terms of the percentage the panel has slid up/down. Recommended values are within 0.0 and 1.0 where 0.0 is no parallax and 1.0 mimics a one-to-one scrolling effect. Defaults to a 10% parallax. |
isDraggable | Allows toggling of draggability of the SlidingUpPanel. Set this to false to prevent the user from being able to drag the panel up and down. Defaults to true. |
slideDirection | Either SlideDirection.UP or SlideDirection.DOWN . Indicates which way the panel should slide. Defaults to UP . If set to DOWN , the panel attaches itself to the top of the screen and is fully opened when the user swipes down on the panel. |
defaultPanelState | The default state of the panel; either PanelState.OPEN or PanelState.CLOSED . This value defaults to PanelState.CLOSED which indicates that the panel is in the closed position and must be opened. PanelState.OPEN indicates that by default the Panel is open and must be swiped closed by the user. |
Darkening the Body as the Panel Opens #
If desired, the body
can be darkened as the panel is opened by setting backdropEnabled
to true
. You can also customize the backdropColor
, backdropOpacity
, and backdropTapClosesPanel
. For example:
@override
Widget build(BuildContext context){
return Material(
child: SlidingUpPanel(
backdropEnabled: true,
panel: Center(
child: Text("This is the sliding Widget"),
),
body: Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
),
),
);
}
Notice how the Scaffold
is nested inside of the SlidingUpPanel
. This because the backdrop is rendered only over the body
of the SlidingUpPanel
. As a result, if we want the backdrop
to appear over the AppBar
, then we must nest the Scaffold
this way.
Displaying a Different Child When the Panel is Closed #
By assigning a non-null Widget to the collapsed
property, you can add a Widget that displays overtop the panel
when collapsed. As the panel
is opened, this Widget will fade out to display the panel
underneath. For example:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
panel: Center(
child: Text("This is the sliding Widget"),
),
collapsed: Container(
color: Colors.blueGrey,
child: Center(
child: Text(
"This is the collapsed Widget",
style: TextStyle(color: Colors.white),
),
),
),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
),
);
}
Rounding the Borders #
Modern design principles (especially in the Material Design Refresh) emphasize rounded borders. A similar effect can be easily achieved by providing a non-null BorderRadiusGeometry
to the borderRadius
property. Note that this only curves the border on the underlying panel sheet: any children passed to panel
or collapsed
must also have their borders curved separately in order to achieve a uniform effect. For example:
@override
Widget build(BuildContext context) {
BorderRadiusGeometry radius = BorderRadius.only(
topLeft: Radius.circular(24.0),
topRight: Radius.circular(24.0),
);
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
panel: Center(
child: Text("This is the sliding Widget"),
),
collapsed: Container(
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: radius
),
child: Center(
child: Text(
"This is the collapsed Widget",
style: TextStyle(color: Colors.white),
),
),
),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
borderRadius: radius,
),
);
}
Creating A Floating Effect #
To create a fully custom effect, the default panel sheet can be completely hidden and only the children rendered (i.e. only body
, panel
, and collapsed
are rendered). To do this, set the renderPanelSheet
property to false
. For example, to create a floating effect:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
renderPanelSheet: false,
panel: _floatingPanel(),
collapsed: _floatingCollapsed(),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
),
);
}
Widget _floatingCollapsed(){
return Container(
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.only(topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)),
),
margin: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
child: Center(
child: Text(
"This is the collapsed Widget",
style: TextStyle(color: Colors.white),
),
),
);
}
Widget _floatingPanel(){
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(24.0)),
boxShadow: [
BoxShadow(
blurRadius: 20.0,
color: Colors.grey,
),
]
),
margin: const EdgeInsets.all(24.0),
child: Center(
child: Text("This is the SlidingUpPanel when open"),
),
);
}
Note that a similar effect can be created by simply adding a margin
to the SlidingUpPanel
.
Adding Scrollable Elements to the Sliding Panel #
The panel
itself can contain Scrollable elements. However, it's important to note that when other Scrollable Widgets are nested inside of the panel
, you need to incorporate some empty space (i.e. non-scrolling space) at the top which the user can swipe down on to close the panel
. For example:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
color: Colors.blueGrey,
panel: _scrollingList(),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
),
);
}
Widget _scrollingList(){
return Container(
//adding a margin to the top leaves an area where the user can swipe
//to open/close the sliding panel
margin: const EdgeInsets.only(top: 36.0),
color: Colors.white,
child: ListView.builder(
itemCount: 50,
itemBuilder: (BuildContext context, int i){
return Container(
padding: const EdgeInsets.all(12.0),
child: Text("$i"),
);
},
),
);
}
Using the PanelController
At times, it can be useful to manually change the state of the SlidingUpPanel
. This can be easily achieved by using a PanelController
and attaching it to an instance of the SlidingUpPanel
. Note that since the PanelController
modifies the state of a SlidingUpPanel
, these methods can only be called after the SlidingUpPanel
has been rendered.
Methods | Description |
---|---|
open() | Opens the sliding panel fully (i.e. to the maxHeight ) |
close() | Closes the sliding panel to its collapsed state (i.e. to the minHeight ) |
hide() | Hides the sliding panel (i.e. is invisible) |
show() | Shows the sliding panel in its collapsed state (i.e. "un-hide" the sliding panel) |
setPanelPosition(double value) | Sets the panel position (without animation). The value must between 0.0 and 1.0 where 0.0 is fully collapsed and 1.0 is completely open. |
animatePanelToPosition(double value) | Animates the panel position to the value. The value must between 0.0 and 1.0 where 0.0 is fully collapsed and 1.0 is completely open. |
getPanelPosition() | Gets the current panel position. Returns the % offset from collapsed state to the open state as a decimal between 0.0 and 1.0 where 0.0 is fully collapsed and 1.0 is full open. |
isPanelAnimating() | Returns whether or not the panel is currently animating. |
isPanelOpen() | Returns whether or not the panel is open. |
isPanelClosed() | Returns whether or not the panel is collapsed. |
isPanelShown() | Returns whether or not the panel is shown/hidden. |
PanelController _pc = new PanelController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
controller: _pc,
panel: Center(
child: Text("This is the sliding Widget"),
),
body: _body(),
),
);
}
Widget _body(){
return Container(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("Open"),
onPressed: () => _pc.open(),
),
RaisedButton(
child: Text("Close"),
onPressed: () => _pc.close(),
),
RaisedButton(
child: Text("Show"),
onPressed: () => _pc.show(),
),
RaisedButton(
child: Text("Hide"),
onPressed: () => _pc.hide(),
),
],
),
);
}
[0.3.6] - [September 25, 2019]
Fixes #
- Fixed issues #54, #59 where panel listeners would be called before UI was rendered (related to
defaultPanelState
)
Documentation #
- Updated the documentation to reflect fixes
[0.3.5] - [August 31, 2019]
Features #
- Added the
defaultPanelState
property that changes whether the panel is either open or closed by default (PanelState.OPEN
orPanelState.CLOSED
)
Documentation #
- Updated the documentation to reflect new features
[0.3.4] - [April 16, 2019]
Features #
- Added the
slideDirection
property that changes how the panel slides open (either up or down)
Documentation #
- Updated the documentation to reflect new features
[0.3.3] - [April 6, 2019]
Features #
- Added the
isDraggable
property that allows/prevents dragging of the panel
Documentation #
- Updated the documentation to reflect new features
[0.3.2] - [April 5, 2019]
Documentation #
- Fixed problem where images would wrap on pub (instead of displaying on one line)
[0.3.1] - [April 5, 2019]
Features #
- Configuration options to
SlidingUpPanel
parallaxEnabled
parallaxOffset
Documentation #
- Created a new example app (Maps)
- Updated documentation to reflect new features
[0.3.0] - April 2, 2019
Features #
-
Added ability to close the
panel
when the backdrop is tapped -
Added callbacks to the
SlidingUpPanel
onPanelSlide
onPanelOpened
onPanelClosed
-
Added methods to the
PanelController
setPanelPosition
animatePanelToPosition
getPanelPosition
isPanelAnimating
isPanelOpen
isPanelClosed
isPanelShown
Bug Fixes #
- Fixed issue where the
collapsed
widget would accept touch events even when invisible (i.e. even when the panel was fully open)
Documentation #
- Updated documentation to reflect new features
- Added clarification on
PanelController
lifecycle - Added an explanation about nesting the
Scaffold
when displaying a backdrop
[0.2.0] - April 1, 2019
Added the backdrop feature:
- Body darkens as the panel opens
- The backdrop color is customizable
- The backdrop opacity is also customizable
- Off by default
Other changes:
- Removed the README from the example app (pub will display the code on the website now)
- Specified Dart as the language in the README code snippets
[0.1.2] - March 31, 2019
- Updated documentation to be more comprehensive
[0.1.1] - March 31, 2019
- Added a CHANGELOG file
[0.1.0] - March 31, 2019
This is the initial release of the sliding_up_panel package. This includes features such as
- A sliding up panel that responds to user gestures
- Customizing the look and feel of the sliding panel
- Manually controlling the sliding panel via the PanelController
/*
Name: Akshath Jain
Date: 3/18/19
Purpose: Example app that implements the package: sliding_up_panel
Copyright: © 2019, Akshath Jain. All rights reserved.
Licensing: More information can be found here: https://github.com/akshathjain/sliding_up_panel/blob/master/LICENSE
*/
import 'package:flutter/material.dart';
import 'package:sliding_up_panel/sliding_up_panel.dart';
import 'package:flutter/services.dart';
// uncomment the following lines before running; there's an ongoing issue with
// pub that causes warnings to be thrown when analyzing nested flutter
// packages - issue #17168, https://github.com/flutter/flutter/issues/17168
// import 'package:flutter_map/flutter_map.dart';
// import 'package:latlong/latlong.dart';
// import 'package:cached_network_image/cached_network_image.dart';
void main() => runApp(SlidingUpPanelExample());
class SlidingUpPanelExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: Colors.grey[200],
systemNavigationBarIconBrightness: Brightness.dark,
systemNavigationBarDividerColor: Colors.black,
));
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'SlidingUpPanel Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final double _initFabHeight = 120.0;
double _fabHeight;
double _panelHeightOpen = 575.0;
double _panelHeightClosed = 95.0;
@override
void initState(){
super.initState();
_fabHeight = _initFabHeight;
}
@override
Widget build(BuildContext context){
return Material(
child: Stack(
alignment: Alignment.topCenter,
children: <Widget>[
SlidingUpPanel(
maxHeight: _panelHeightOpen,
minHeight: _panelHeightClosed,
parallaxEnabled: true,
parallaxOffset: .5,
body: _body(),
panel: _panel(),
borderRadius: BorderRadius.only(topLeft: Radius.circular(18.0), topRight: Radius.circular(18.0)),
onPanelSlide: (double pos) => setState((){
_fabHeight = pos * (_panelHeightOpen - _panelHeightClosed) + _initFabHeight;
}),
),
// the fab
Positioned(
right: 20.0,
bottom: _fabHeight,
child: FloatingActionButton(
child: Icon(
Icons.gps_fixed,
color: Theme.of(context).primaryColor,
),
onPressed: (){},
backgroundColor: Colors.white,
),
),
//the SlidingUpPanel Titel
Positioned(
top: 42.0,
child: Container(
padding: const EdgeInsets.fromLTRB(24.0, 18.0, 24.0, 18.0),
child: Text(
"SlidingUpPanel Example",
style: TextStyle(
fontWeight: FontWeight.w500
),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
boxShadow: [BoxShadow(
color: Color.fromRGBO(0, 0, 0, .25),
blurRadius: 16.0
)],
),
),
),
],
),
);
}
Widget _panel(){
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 12.0,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 30,
height: 5,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.all(Radius.circular(12.0))
),
),
],
),
SizedBox(height: 18.0,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Explore Pittsburgh",
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 24.0,
),
),
],
),
SizedBox(height: 36.0,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_button("Popular", Icons.favorite, Colors.blue),
_button("Food", Icons.restaurant, Colors.red),
_button("Events", Icons.event, Colors.amber),
_button("More", Icons.more_horiz, Colors.green),
],
),
SizedBox(height: 36.0,),
Container(
padding: const EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Images", style: TextStyle(fontWeight: FontWeight.w600,)),
SizedBox(height: 12.0,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CachedNetworkImage(
imageUrl: "https://images.fineartamerica.com/images-medium-large-5/new-pittsburgh-emmanuel-panagiotakis.jpg",
height: 120.0,
width: (MediaQuery.of(context).size.width - 48) / 2 - 2,
fit: BoxFit.cover,
),
CachedNetworkImage(
imageUrl: "https://cdn.pixabay.com/photo/2016/08/11/23/48/pnc-park-1587285_1280.jpg",
width: (MediaQuery.of(context).size.width - 48) / 2 - 2,
height: 120.0,
fit: BoxFit.cover,
),
],
),
],
),
),
SizedBox(height: 36.0,),
Container(
padding: const EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("About", style: TextStyle(fontWeight: FontWeight.w600,)),
SizedBox(height: 12.0,),
Text(
"Pittsburgh is a city in the Commonwealth of Pennsylvania "
"in the United States, and is the county seat of Allegheny County. "
"As of 2017, a population of 305,704 lives within the city limits, "
"making it the 63rd-largest city in the U.S. The metropolitan population "
"of 2,353,045 is the largest in both the Ohio Valley and Appalachia, "
"the second-largest in Pennsylvania (behind Philadelphia), "
"and the 26th-largest in the U.S. Pittsburgh is located in the "
"south west of the state, at the confluence of the Allegheny, "
"Monongahela, and Ohio rivers, Pittsburgh is known both as 'the Steel City' "
"for its more than 300 steel-related businesses and as the 'City of Bridges' "
"for its 446 bridges. The city features 30 skyscrapers, two inclined railways, "
"a pre-revolutionary fortification and the Point State Park at the "
"confluence of the rivers. The city developed as a vital link of "
"the Atlantic coast and Midwest, as the mineral-rich Allegheny "
"Mountains made the area coveted by the French and British "
"empires, Virginians, Whiskey Rebels, and Civil War raiders. ",
maxLines: 7,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
);
}
Widget _button(String label, IconData icon, Color color){
return Column(
children: <Widget>[
Container(
padding: const EdgeInsets.all(16.0),
child: Icon(
icon,
color: Colors.white,
),
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
boxShadow: [BoxShadow(
color: Color.fromRGBO(0, 0, 0, 0.15),
blurRadius: 8.0,
)]
),
),
SizedBox(height: 12.0,),
Text(label),
],
);
}
Widget _body(){
return FlutterMap(
options: MapOptions(
center: LatLng(40.441589, -80.010948),
zoom: 13,
maxZoom: 15,
),
layers: [
TileLayerOptions(
urlTemplate: "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png"
),
MarkerLayerOptions(
markers: [
Marker(
point: LatLng(40.441753, -80.011476),
builder: (ctx) => Icon(
Icons.location_on,
color: Colors.blue,
size: 48.0,
),
height: 60
),
]
),
],
);
}
}
Use this package as a library
1. Depend on it
Add this to your package's pubspec.yaml file:
dependencies:
sliding_up_panel: ^0.3.6
2. Install it
You can install packages from the command line:
with Flutter:
$ flutter pub get
Alternatively, your editor might support flutter pub get
.
Check the docs for your editor to learn more.
3. Import it
Now in your Dart code, you can use:
import 'package:sliding_up_panel/sliding_up_panel.dart';
Popularity:
Describes how popular the package is relative to other packages.
[more]
|
98
|
Health:
Code health derived from static analysis.
[more]
|
100
|
Maintenance:
Reflects how tidy and up-to-date the package is.
[more]
|
100
|
Overall:
Weighted score of the above.
[more]
|
99
|
We analyzed this package on Dec 7, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:
- Dart: 2.6.1
- pana: 0.12.21
- Flutter: 1.9.1+hotfix.6
Platforms
Detected platforms: Flutter
References Flutter, and has no conflicting libraries.
Health suggestions
Format lib/sliding_up_panel.dart
.
Run flutter format
to format lib/sliding_up_panel.dart
.
Format lib/src/panel.dart
.
Run flutter format
to format lib/src/panel.dart
.
Dependencies
Package | Constraint | Resolved | Available |
---|---|---|---|
Direct dependencies | |||
Dart SDK | >=2.1.0 <3.0.0 | ||
flutter | 0.0.0 | ||
Transitive dependencies | |||
collection | 1.14.11 | 1.14.12 | |
meta | 1.1.7 | 1.1.8 | |
sky_engine | 0.0.99 | ||
typed_data | 1.1.6 | ||
vector_math | 2.0.8 | ||
Dev dependencies | |||
flutter_test |