Skip to content

Commit

Permalink
Added scrollable sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcinusX committed Jan 21, 2020
1 parent 1f7b11d commit bd077be
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 12 deletions.
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -11,3 +11,7 @@ Described in following post:

## The implementation:
![Implementation](https://user-images.githubusercontent.com/16286046/58473033-e8ce3500-8147-11e9-831b-78f9cf86addb.gif)

> In home_page.dart you can replace ExhibitionBottomSheet with ScrollableExhibitionSheet to achieve see different implementation with full Listview.
![Scrollable](https://user-images.githubusercontent.com/16286046/72800351-7286c180-3c47-11ea-8a3e-a2e24c9208c7.gif)
4 changes: 3 additions & 1 deletion lib/home_page.dart
Expand Up @@ -3,6 +3,8 @@ import 'package:buy_tickets_design/sliding_cards.dart';
import 'package:buy_tickets_design/tabs.dart';
import 'package:flutter/material.dart';

import 'exhibition_bottom_sheet.dart';

class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Expand All @@ -23,7 +25,7 @@ class HomePage extends StatelessWidget {
],
),
),
ExhibitionBottomSheet(),
ExhibitionBottomSheet(), //use this or ScrollableExhibitionSheet
],
),
);
Expand Down
267 changes: 267 additions & 0 deletions lib/scrollable_exhibition_bottom_sheet.dart
@@ -0,0 +1,267 @@
import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';

///Notice that by default this class is not used
///Go to [home_page.dart] and replace [ExhibitionBottomSheet] with [ScrollableExhibitionSheet] to use it
class ScrollableExhibitionSheet extends StatefulWidget {
@override
_ScrollableExhibitionSheetState createState() =>
_ScrollableExhibitionSheetState();
}

class _ScrollableExhibitionSheetState extends State<ScrollableExhibitionSheet> {
double initialPercentage = 0.15;

@override
Widget build(BuildContext context) {
return Positioned.fill(
child: DraggableScrollableSheet(
minChildSize: initialPercentage,
initialChildSize: initialPercentage,
builder: (context, scrollController) {
return AnimatedBuilder(
animation: scrollController,
builder: (context, child) {
double percentage = initialPercentage;
if (scrollController.hasClients) {
percentage = (scrollController.position.viewportDimension) /
(MediaQuery.of(context).size.height);
}
double scaledPercentage =
(percentage - initialPercentage) / (1 - initialPercentage);
return Container(
padding: const EdgeInsets.only(left: 32),
decoration: const BoxDecoration(
color: Color(0xFF162A49),
borderRadius: BorderRadius.vertical(top: Radius.circular(32)),
),
child: Stack(
children: <Widget>[
Opacity(
opacity: percentage == 1 ? 1 : 0,
child: ListView.builder(
padding: EdgeInsets.only(right: 32, top: 128),
controller: scrollController,
itemCount: 20,
itemBuilder: (context, index) {
Event event = events[index % 3];
return MyEventItem(
event: event,
percentageCompleted: percentage,
);
},
),
),
...events.map((event) {
int index = events.indexOf(event);
int heightPerElement = 120 + 8 + 8;
double widthPerElement =
40 + percentage * 80 + 8 * (1 - percentage);
double leftOffset = widthPerElement *
(index > 4 ? index + 2 : index) *
(1 - scaledPercentage);
return Positioned(
top: 44.0 +
scaledPercentage * (128 - 44) +
index * heightPerElement * scaledPercentage,
left: leftOffset,
right: 32 - leftOffset,
child: IgnorePointer(
ignoring: true,
child: Opacity(
opacity: percentage == 1 ? 0 : 1,
child: MyEventItem(
event: event,
percentageCompleted: percentage,
),
),
),
);
}),
SheetHeader(
fontSize: 14 + percentage * 8,
topMargin:
16 + percentage * MediaQuery.of(context).padding.top,
),
MenuButton(),
],
),
);
},
);
},
),
);
}
}

class MyEventItem extends StatelessWidget {
final Event event;
final double percentageCompleted;

const MyEventItem({Key key, this.event, this.percentageCompleted})
: super(key: key);

@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Transform.scale(
alignment: Alignment.topLeft,
scale: 1 / 3 + 2 / 3 * percentageCompleted,
child: SizedBox(
height: 120,
child: Row(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.horizontal(
left: Radius.circular(16),
right: Radius.circular(16 * (1 - percentageCompleted)),
),
child: Image.asset(
'assets/${event.assetName}',
width: 120,
height: 120,
fit: BoxFit.cover,
),
),
Expanded(
child: Opacity(
opacity: max(0, percentageCompleted * 2 - 1),
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.horizontal(right: Radius.circular(16)),
color: Colors.white,
),
padding: EdgeInsets.all(8),
child: _buildContent(),
),
),
)
],
),
),
),
);
}

Widget _buildContent() {
return Column(
children: <Widget>[
Text(event.title, style: TextStyle(fontSize: 16)),
SizedBox(height: 8),
Row(
children: <Widget>[
Text(
'1 ticket',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 12,
color: Colors.grey.shade600,
),
),
SizedBox(width: 8),
Text(
event.date,
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 12,
color: Colors.grey,
),
),
],
),
Spacer(),
Row(
children: <Widget>[
Icon(Icons.place, color: Colors.grey.shade400, size: 16),
Text(
'Science Park 10 25A',
style: TextStyle(color: Colors.grey.shade400, fontSize: 13),
)
],
)
],
);
}
}

final List<Event> events = [
Event('steve-johnson.jpeg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('efe-kurnaz.jpg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('rodion-kutsaev.jpeg', 'Dawan District Guangdong Hong Kong', '4.28-31'),
Event('steve-johnson.jpeg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('efe-kurnaz.jpg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('rodion-kutsaev.jpeg', 'Dawan District Guangdong Hong Kong', '4.28-31'),
Event('steve-johnson.jpeg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('efe-kurnaz.jpg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('rodion-kutsaev.jpeg', 'Dawan District Guangdong Hong Kong', '4.28-31'),
Event('steve-johnson.jpeg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('efe-kurnaz.jpg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('rodion-kutsaev.jpeg', 'Dawan District Guangdong Hong Kong', '4.28-31'),
Event('steve-johnson.jpeg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('efe-kurnaz.jpg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('rodion-kutsaev.jpeg', 'Dawan District Guangdong Hong Kong', '4.28-31'),
Event('steve-johnson.jpeg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('efe-kurnaz.jpg', 'Shenzhen GLOBAL DESIGN AWARD 2018', '4.20-30'),
Event('rodion-kutsaev.jpeg', 'Dawan District Guangdong Hong Kong', '4.28-31'),
];

class Event {
final String assetName;
final String title;
final String date;

Event(this.assetName, this.title, this.date);
}

class SheetHeader extends StatelessWidget {
final double fontSize;
final double topMargin;

const SheetHeader(
{Key key, @required this.fontSize, @required this.topMargin})
: super(key: key);

@override
Widget build(BuildContext context) {
return Positioned(
left: 0,
right: 32,
child: IgnorePointer(
child: Container(
padding: EdgeInsets.only(top: topMargin, bottom: 12),
decoration: const BoxDecoration(
color: Color(0xFF162A49),
),
child: Text(
'Booked Exhibition',
style: TextStyle(
color: Colors.white,
fontSize: fontSize,
fontWeight: FontWeight.w500,
),
),
),
),
);
}
}

class MenuButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Positioned(
right: 12,
bottom: 24,
child: Icon(
Icons.menu,
color: Colors.white,
size: 28,
),
);
}
}

0 comments on commit bd077be

Please sign in to comment.