Skip to content

Instantly share code, notes, and snippets.

@aloisdeniel
Last active August 30, 2021 12:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aloisdeniel/5564b2da164aadc107c54b07072e2783 to your computer and use it in GitHub Desktop.
Save aloisdeniel/5564b2da164aadc107c54b07072e2783 to your computer and use it in GitHub Desktop.
Expand padding while scroll
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Expanded scroll demo',
theme: ThemeData.dark(),
home: const Home(),
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Column(
children: [
ElevatedButton(
onPressed: () => SettingsPage.show(context, 100),
child: Text('Long settings'),
),
ElevatedButton(
onPressed: () => SettingsPage.show(context, 5),
child: Text('Small settings'),
),
],
),
);
}
}
class SettingsPage extends StatelessWidget {
const SettingsPage({
Key? key,
required this.tileCount,
}) : super(key: key);
final int tileCount;
static Future<void> show(BuildContext context, int tileCount) {
return Navigator.push(
context,
PageRouteBuilder(
opaque: false,
barrierDismissible: true,
barrierColor: Colors.black38,
pageBuilder: (context, anim1, anim2) => SlideTransition(
position: Tween(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: anim1,
curve: Curves.easeOut,
),
),
child: SettingsPage(
tileCount: tileCount,
),
),
),
);
}
@override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
return ExpandedScrollContainer(
builder: (context, controller) => Material(
child: CustomScrollView(
controller: controller,
shrinkWrap: true,
physics: ClampingScrollPhysics(),
slivers: [
SliverAppBar(
pinned: true,
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
title: Text(
'Settings',
),
),
SliverPadding(
padding: mediaQuery.padding.copyWith(top: 0),
sliver: SliverList(
delegate: SliverChildListDelegate(
[
for (var i = 0; i < tileCount; i++)
ListTile(
title: Text('Tile $i'),
),
],
),
),
)
],
),
),
);
}
}
class SettingsBar extends StatelessWidget {
const SettingsBar({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
return Padding(
padding:
mediaQuery.padding.copyWith(bottom: 0) + const EdgeInsets.all(10),
child: IconButton(
icon: Icon(
Icons.close,
),
onPressed: () => Navigator.pop(context),
),
);
}
}
typedef AnimatedScrollWidgetBuilder = Widget Function(
BuildContext context,
ScrollController controller,
);
class ExpandedScrollContainer extends StatefulWidget {
const ExpandedScrollContainer({
Key? key,
required this.builder,
this.initialBorderRadius = const BorderRadius.all(Radius.circular(10)),
this.removePaddingExtentOffset = 200.0,
this.initialPadding = const EdgeInsets.only(
left: 20,
right: 20,
top: 60,
),
}) : super(key: key);
final BorderRadius initialBorderRadius;
final EdgeInsets initialPadding;
final double removePaddingExtentOffset;
final AnimatedScrollWidgetBuilder builder;
@override
_ExpandedScrollContainerState createState() =>
_ExpandedScrollContainerState();
}
class _ExpandedScrollContainerState extends State<ExpandedScrollContainer> {
final ScrollController controller = ScrollController();
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final totalPadding = mediaQuery.padding + widget.initialPadding;
return Dismissible(
key: Key('Dismiss'),
direction: DismissDirection.down,
onDismissed: (_) => Navigator.pop(context),
child: AnimatedBuilder(
animation: controller,
builder: (context, _) {
final offset = controller.hasClients
? controller.offset
: controller.initialScrollOffset;
print(offset);
final amount =
(offset / widget.removePaddingExtentOffset).clamp(0.0, 1.0);
return Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsets.lerp(
totalPadding.copyWith(bottom: 0),
EdgeInsets.zero,
amount,
)!,
child: ClipRRect(
borderRadius: BorderRadius.lerp(
widget.initialBorderRadius,
BorderRadius.zero,
amount,
),
child: MediaQuery(
data: mediaQuery.copyWith(
padding: EdgeInsets.lerp(
mediaQuery.padding.copyWith(top: 0, left: 0, right: 0),
mediaQuery.padding,
amount,
)!,
),
child: widget.builder(context, controller),
),
),
),
);
},
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment