-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add basic static SBBHeaderbox and SBBHeaderboxFlap * feat: first version of SBBHeaderbox done * feat: defer default and large to own widgets and add doc * feat: SBBSliverHeaderbox first version done * feat: add semantics * test: add tests for SBBHeaderbox * docs: add SBBHeaderbox to Changelog
- Loading branch information
1 parent
97074fd
commit 172b257
Showing
15 changed files
with
947 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart'; | ||
|
||
class HeaderBoxPage extends StatefulWidget { | ||
const HeaderBoxPage({super.key}); | ||
|
||
@override | ||
State<HeaderBoxPage> createState() => _HeaderBoxPageState(); | ||
} | ||
|
||
class _HeaderBoxPageState extends State<HeaderBoxPage> { | ||
final items = <TabBarItem>[ | ||
_DemoItem(0, SBBIcons.paragraph_small), | ||
_DemoItem(1, SBBIcons.lock_closed_small), | ||
_DemoItem(2, SBBIcons.arrows_up_down_small), | ||
]; | ||
|
||
late PageController _pageViewController; | ||
late TabBarController _tabBarController; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
_pageViewController = PageController(); | ||
_tabBarController = TabBarController(items.first); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
super.dispose(); | ||
_pageViewController.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Column( | ||
children: [ | ||
Expanded( | ||
child: PageView( | ||
physics: NeverScrollableScrollPhysics(), | ||
controller: _pageViewController, | ||
children: <Widget>[ | ||
DesignGuidelinePage(), | ||
StaticPage(), | ||
ScrollablePage(), | ||
], | ||
), | ||
), | ||
SBBTabBar( | ||
items: items, | ||
onTabChanged: (task) async { | ||
task.then((value) => _handlePageViewChanged(int.parse(value.id))); | ||
}, | ||
controller: _tabBarController, | ||
onTap: (tab) {}, | ||
) | ||
], | ||
); | ||
} | ||
|
||
void _handlePageViewChanged(int newPageIndex) { | ||
_pageViewController.animateToPage( | ||
newPageIndex, | ||
duration: const Duration(milliseconds: 400), | ||
curve: Curves.easeInOut, | ||
); | ||
} | ||
} | ||
|
||
class DesignGuidelinePage extends StatelessWidget { | ||
const DesignGuidelinePage({ | ||
super.key, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final sbbToast = SBBToast.of(context); | ||
return Column( | ||
children: [ | ||
const SizedBox(height: sbbDefaultSpacing), | ||
const SBBListHeader('Default'), | ||
SBBHeaderbox( | ||
title: 'Title', | ||
leadingIcon: SBBIcons.dog_small, | ||
secondaryLabel: 'Subtext', | ||
flap: SBBHeaderboxFlap( | ||
title: 'Additional text or information', | ||
leadingIcon: SBBIcons.sign_exclamation_point_small, | ||
trailingIcon: SBBIcons.circle_information_small_small, | ||
), | ||
trailingWidget: SBBTertiaryButtonSmall( | ||
label: 'Label', | ||
icon: SBBIcons.dog_small, | ||
onPressed: () => sbbToast.show(message: 'Default pressed', bottom: sbbDefaultSpacing * 6), | ||
), | ||
), | ||
const SizedBox(height: sbbDefaultSpacing), | ||
const SBBListHeader('Large'), | ||
SBBHeaderbox.large( | ||
title: 'Title', | ||
leadingIcon: SBBIcons.dog_medium, | ||
secondaryLabel: 'Subtext', | ||
trailingWidget: SBBIconButtonLarge( | ||
icon: SBBIcons.dog_small, | ||
onPressed: () => sbbToast.show(message: 'Large pressed', bottom: sbbDefaultSpacing * 6), | ||
), | ||
), | ||
const SizedBox(height: sbbDefaultSpacing), | ||
const SBBListHeader('Custom'), | ||
const SBBHeaderbox.custom( | ||
padding: EdgeInsets.zero, | ||
flap: SBBHeaderboxFlap.custom(child: Center(child: Text('Choooooo!', style: SBBTextStyles.extraSmallBold))), | ||
child: Center(child: Text('🚂。🚋。🚋。🚋。🚋˙⊹⁺.')), | ||
) | ||
], | ||
); | ||
} | ||
} | ||
|
||
class StaticPage extends StatefulWidget { | ||
const StaticPage({ | ||
super.key, | ||
}); | ||
|
||
@override | ||
State<StaticPage> createState() => _StaticPageState(); | ||
} | ||
|
||
class _StaticPageState extends State<StaticPage> { | ||
bool _headerBoxExpanded = false; | ||
final _expandedHeight = 340.0; | ||
final _collapsedHeight = 0.0; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final isDark = Theme.of(context).brightness == Brightness.dark; | ||
return Stack( | ||
children: [ | ||
_body(), | ||
SBBHeaderbox.custom( | ||
child: Column( | ||
mainAxisSize: MainAxisSize.min, | ||
children: [ | ||
Row( | ||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
children: [ | ||
Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text('Static Screen', style: SBBTextStyles.mediumBold), | ||
Text( | ||
'Click to expand Headerbox.', | ||
style: | ||
SBBTextStyles.smallLight.copyWith(color: isDark ? SBBColors.graphite : SBBColors.granite), | ||
) | ||
], | ||
), | ||
SBBTertiaryButtonSmall( | ||
label: 'Expand', onPressed: () => setState(() => _headerBoxExpanded = !_headerBoxExpanded)), | ||
], | ||
), | ||
AnimatedContainer( | ||
curve: Curves.easeInOut, | ||
height: _headerBoxExpanded ? _expandedHeight : _collapsedHeight, | ||
duration: Durations.long4, | ||
), | ||
], | ||
), | ||
), | ||
], | ||
); | ||
} | ||
|
||
Center _body() { | ||
return Center( | ||
child: SBBMessage( | ||
title: 'Cover me!', | ||
description: 'This screen is non scrollable.\nUsing a Stack, the Headerbox will simply lay on top of it.', | ||
), | ||
); | ||
} | ||
} | ||
|
||
class ScrollablePage extends StatefulWidget { | ||
const ScrollablePage({super.key}); | ||
|
||
@override | ||
State<ScrollablePage> createState() => _ScrollablePageState(); | ||
} | ||
|
||
class _ScrollablePageState extends State<ScrollablePage> { | ||
bool _headerBoxExpanded = false; | ||
final _expandedHeight = 300.0; | ||
final _collapsedHeight = 0.0; | ||
@override | ||
Widget build(BuildContext context) { | ||
final sbbToast = SBBToast.of(context); | ||
final isDark = Theme.of(context).brightness == Brightness.dark; | ||
return CustomScrollView( | ||
slivers: [ | ||
SBBSliverHeaderbox.custom( | ||
child: Column( | ||
mainAxisSize: MainAxisSize.min, | ||
children: [ | ||
Row( | ||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
children: [ | ||
Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text('Scrollable Screen', style: SBBTextStyles.mediumBold), | ||
Text( | ||
'Click to expand Headerbox.', | ||
style: | ||
SBBTextStyles.smallLight.copyWith(color: isDark ? SBBColors.graphite : SBBColors.granite), | ||
) | ||
], | ||
), | ||
SBBTertiaryButtonSmall( | ||
label: 'Expand', onPressed: () => setState(() => _headerBoxExpanded = !_headerBoxExpanded)), | ||
], | ||
), | ||
AnimatedContainer( | ||
curve: Curves.easeInOut, | ||
height: _headerBoxExpanded ? _expandedHeight : _collapsedHeight, | ||
duration: Durations.long4, | ||
), | ||
], | ||
), | ||
), | ||
SliverList.builder( | ||
itemCount: 60, | ||
itemBuilder: (context, index) => SBBListItem( | ||
title: 'Item $index', | ||
onPressed: () => sbbToast.show(message: 'Pressed Item $index', bottom: sbbDefaultSpacing * 6), | ||
), | ||
) | ||
], | ||
); | ||
} | ||
} | ||
|
||
class _DemoItem extends TabBarItem { | ||
_DemoItem(int id, IconData icon) : super(id.toString(), icon); | ||
|
||
@override | ||
String translate(BuildContext context) => ''; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export 'sbb_headerbox.dart'; | ||
export 'sbb_headerbox_flap.dart'; | ||
export 'sbb_sliver_headerbox.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import 'dart:math'; | ||
|
||
import 'package:flutter/rendering.dart'; | ||
|
||
class RenderSliverPinHeader extends RenderSliverSingleBoxAdapter { | ||
@override | ||
void performLayout() { | ||
child!.layout(constraints.asBoxConstraints(), parentUsesSize: true); | ||
double childExtent; | ||
switch (constraints.axis) { | ||
case Axis.horizontal: | ||
childExtent = child!.size.width; | ||
break; | ||
case Axis.vertical: | ||
childExtent = child!.size.height; | ||
break; | ||
} | ||
final paintedChildExtent = min( | ||
childExtent, | ||
constraints.remainingPaintExtent - constraints.overlap, | ||
); | ||
geometry = SliverGeometry( | ||
paintExtent: paintedChildExtent, | ||
maxPaintExtent: childExtent, | ||
maxScrollObstructionExtent: childExtent, | ||
paintOrigin: constraints.overlap, | ||
scrollExtent: childExtent, | ||
layoutExtent: max(0.0, paintedChildExtent - constraints.scrollOffset), | ||
hasVisualOverflow: paintedChildExtent < childExtent, | ||
); | ||
} | ||
|
||
@override | ||
double childMainAxisPosition(RenderBox child) { | ||
return 0; | ||
} | ||
} |
Oops, something went wrong.