A simple, lightweight, and fully customizable widget designed to shrink and expand a view/screen/widget programmatically. ✨
Similar to how iOS shrinks pages behind content in the foreground. Mainly intended to wrap Scaffold widgets.
-> Submit an issue here. -> Create a pull request here. -> Contact me via email here.
Features 🔥
- Shrinks and expands a view/screen/widget.
- Controlled programmatically with a
ShrinkingViewController
. - Requires only a few lines of code - simplicity is beautiful.
Gif Demos 📸
Getting Started 📜
-
Install and import the package.
$ flutter pub add shrinking_view import 'package:shrinking_view/shrinking_view.dart';
-
In your app, create a
ShrinkingViewController
to pass to theShrinkingView
like so:late ShrinkingViewController controller; // <--- Create a ShrinkingViewController. @override void initState() { controller = ShrinkingViewController(tickerProvider: this); // <-- Initialize it with a TickerProvider. super.initState(); }
Note: Make sure your class is using either a
SingleTickerProviderStateMixin
orTickerProviderStateMixin
so you can pass inthis
as theTickerProvider
as required by theShrinkingViewController
. -
Wrap your
Scaffold
(or any widget, butScaffold
is recommended) inside theShrinkingView
, passing in thecontroller
you just created.return ShrinkingView( controller: controller, child: const Scaffold( body: <YOUR_APP>, ), );
-
You're done! You can now call
controller.<SOME_METHOD>
to control yourShrinkingView
! 🎉
ShrinkingViewController
Methods 🛠️
shrink() => void
: Starts the shrinking animation.expand() => void
: Starts the expanding animation.isShrunk() => bool
: Returnstrue
if the last static state was shrunk. For example, if theShrinkingView
is currently animating, this will reflect the state before the animation began.isExpanded() => bool
: Returnstrue
if the last static state was expanded. For example, if theShrinkingView
is currently animating, this will reflect the state before the animation began.isAnimating() => bool
: Returnstrue
if theShrinkingView
is currently animating.isShrinkingCurrently() => bool
: Returnstrue
if theShrinkingView
is currently shrinking.isExpandingCurrently() => bool
: Returnstrue
if theShrinkingView
is currently expanding.
ShrinkingView
Properties 🛠️
- (required)
controller
: TheShrinkingViewController
you must pass in order to control theShrinkingView
. No default value, as it's a required field. - (required)
child
: TheWidget
you must pass that wraps what you want to be shrunk/expanded. Usually, this is aScaffold
. No default value, as it's a required field. topLeftSquared
: Whether the top left of the widget should have noBorderRadius
applied. Default:false
.topRightSquared
: Whether the top right of the widget should have noBorderRadius
applied. Default:false
.bottomRightSquared
: Whether the bottom right of the widget should have noBorderRadius
applied. Default:true
.bottomLeftSquared
: Whether the bottom left of the widget should have noBorderRadius
. Default:true
.safeAreaTop
: Whether the widget should automatically add a topSafeArea
. Default:false
.safeAreaBottom
: Whether the widget should automatically add a bottomSafeArea
. Default:false
.safeAreaLeft
: Whether the widget should automatically add a leftSafeArea
. Default:false
.safeAreaRight
: Whether the widget should automatically add a rightSafeArea
. Default:false
.backgroundColorWhileAnimating
: The backgroundColor
behind the passedchild
widget that is (usually; depending on your implementation) displayed when a shrinking/expanding animation is occuring. Default:Colors.black
.maintainBottomViewPadding
: Specifies whether theSafeArea
should maintain the bottomMediaQueryData.viewPadding
instead of the bottomMediaQueryData.padding
. Default:true
.shrinkingAnimationCurve
: TheAnimation
Curve
that's used when a shrinking animation is occuring. Default:Curves.decelerate
.expandingAnimationCurve
: TheAnimation
Curve
that's used when an expanding animation is occuring. Default:Curves.linear
.verticalTranslateMultiplier
: The factor by how much down thechild
should translate. This is proportionate to the screen height (translation down: screen height *verticalTranslateMultiplier
). Default:0.055
.scaleMultiplier
: The factor by how much smaller thechild
should get while shrinking. Example:0.5
means thechild
would get 50% smaller,0.25
means it would get 25% smaller. Default:0.04
.borderRadiusValue
: The circularBorderRadius
value each of the 4 corners can animate to (if their respective<Their_Side>Squared
property isfalse
). Default:50.0
.
Example ✍️
import 'package:flutter/material.dart';
import 'package:shrinking_view/shrinking_view.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'shrinking_view package',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
// Ensure you use either SingleTickerProviderStateMixin or TickerProviderStateMixin.
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late ShrinkingViewController controller; // <--- Create a ShrinkingViewController.
@override
void initState() {
controller = ShrinkingViewController(tickerProvider: this); // <-- Initialize it with a TickerProvider.
super.initState();
}
@override
Widget build(BuildContext context) {
return ShrinkingView(
// <--- Wrap your widget (usually a Scaffold) with the ShrinkingView.
controller: controller, // <--- Pass it the controller.
child: Scaffold(
appBar: AppBar(
title: const Text("shrinking_view example"),
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () => controller.expand(), // <--- Expand the ShrinkingView.
child: const Text("Expand (void)"),
),
TextButton(
onPressed: () => controller.shrink(), // <--- Shrink the ShrinkingView.
child: const Text("Shrink (void)"),
),
TextButton(
onPressed: () => print(controller.isShrunk()), // <--- Is it shrunk?
child: const Text("Is shrunk? (bool)"),
),
TextButton(
onPressed: () => print(controller.isExpanded()), // <--- Is it expanded?
child: const Text("Is expanded? (bool)"),
),
TextButton(
onPressed: () => print(controller.isAnimating()), // <--- Is it animating?
child: const Text("Is animating? (bool)"),
),
TextButton(
onPressed: () => print(controller.isShrinkingCurrently()), // <--- Is it currently shrinking?
child: const Text("Is currently shrinking? (bool)"),
),
TextButton(
onPressed: () => print(controller.isExpandingCurrently()), // <--- Is it currently expanding?
child: const Text("Is currently expanding? (bool)"),
),
],
),
),
),
);
}
}
Additional information 📣
The package is always open to improvements and suggestions! Hope you enjoy :)