Inject menu padding into the detail views so they can have their backgrounds overlap with the nav-rail and its curved corner.
This commit is contained in:
parent
0f2e2afd9b
commit
50d4bac794
@ -12,8 +12,9 @@ part 'widgets/_bottom_text_content.dart';
|
|||||||
part 'widgets/_collapsing_carousel_item.dart';
|
part 'widgets/_collapsing_carousel_item.dart';
|
||||||
|
|
||||||
class ArtifactCarouselScreen extends StatefulWidget {
|
class ArtifactCarouselScreen extends StatefulWidget {
|
||||||
|
const ArtifactCarouselScreen({Key? key, required this.type, this.contentPadding = EdgeInsets.zero}) : super(key: key);
|
||||||
final WonderType type;
|
final WonderType type;
|
||||||
const ArtifactCarouselScreen({Key? key, required this.type}) : super(key: key);
|
final EdgeInsets contentPadding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ArtifactCarouselScreen> createState() => _ArtifactScreenState();
|
State<ArtifactCarouselScreen> createState() => _ArtifactScreenState();
|
||||||
@ -82,55 +83,61 @@ class _ArtifactScreenState extends State<ArtifactCarouselScreen> {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// BgCircle
|
Padding(
|
||||||
_buildBgCircle(bottomHeight),
|
padding: widget.contentPadding,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
/// BgCircle
|
||||||
|
_buildBgCircle(bottomHeight),
|
||||||
|
|
||||||
/// Carousel Items
|
/// Carousel Items
|
||||||
PageView.builder(
|
PageView.builder(
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
final wrappedIndex = index % pages.length;
|
final wrappedIndex = index % pages.length;
|
||||||
final child = pages[wrappedIndex];
|
final child = pages[wrappedIndex];
|
||||||
return ValueListenableBuilder<double>(
|
return ValueListenableBuilder<double>(
|
||||||
valueListenable: _currentPage,
|
valueListenable: _currentPage,
|
||||||
builder: (_, value, __) {
|
builder: (_, value, __) {
|
||||||
final int offset = (value.round() - index).abs();
|
final int offset = (value.round() - index).abs();
|
||||||
return _CollapsingCarouselItem(
|
return _CollapsingCarouselItem(
|
||||||
width: itemWidth,
|
width: itemWidth,
|
||||||
indexOffset: min(3, offset),
|
indexOffset: min(3, offset),
|
||||||
onPressed: () => _handleArtifactTap(index),
|
onPressed: () => _handleArtifactTap(index),
|
||||||
title: _artifacts[wrappedIndex].title,
|
title: _artifacts[wrappedIndex].title,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
/// Bottom Text
|
/// Bottom Text
|
||||||
BottomCenter(
|
BottomCenter(
|
||||||
child: ValueListenableBuilder<int>(
|
child: ValueListenableBuilder<int>(
|
||||||
valueListenable: _currentArtifactIndex,
|
valueListenable: _currentArtifactIndex,
|
||||||
builder: (_, value, __) => _BottomTextContent(
|
builder: (_, value, __) => _BottomTextContent(
|
||||||
artifact: _artifacts[value],
|
artifact: _artifacts[value],
|
||||||
height: bottomHeight,
|
height: bottomHeight,
|
||||||
shortMode: shortMode,
|
shortMode: shortMode,
|
||||||
state: this,
|
state: this,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// Header
|
/// Header
|
||||||
AppHeader(
|
AppHeader(
|
||||||
title: $strings.artifactsTitleArtifacts,
|
title: $strings.artifactsTitleArtifacts,
|
||||||
showBackBtn: false,
|
showBackBtn: false,
|
||||||
isTransparent: true,
|
isTransparent: true,
|
||||||
trailing: (context) => CircleBtn(
|
trailing: (context) => CircleBtn(
|
||||||
semanticLabel: $strings.artifactsButtonBrowse,
|
semanticLabel: $strings.artifactsButtonBrowse,
|
||||||
onPressed: _handleSearchTap,
|
onPressed: _handleSearchTap,
|
||||||
child: AppIcon(AppIcons.search),
|
child: AppIcon(AppIcons.search),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,11 @@ part 'widgets/_title_text.dart';
|
|||||||
part 'widgets/_top_illustration.dart';
|
part 'widgets/_top_illustration.dart';
|
||||||
|
|
||||||
class WonderEditorialScreen extends StatefulWidget {
|
class WonderEditorialScreen extends StatefulWidget {
|
||||||
const WonderEditorialScreen(this.data, {Key? key, required this.onScroll}) : super(key: key);
|
const WonderEditorialScreen(this.data, {Key? key, required this.onScroll, required this.contentPadding})
|
||||||
|
: super(key: key);
|
||||||
final WonderData data;
|
final WonderData data;
|
||||||
final void Function(double scrollPos) onScroll;
|
final void Function(double scrollPos) onScroll;
|
||||||
|
final EdgeInsets contentPadding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<WonderEditorialScreen> createState() => _WonderEditorialScreenState();
|
State<WonderEditorialScreen> createState() => _WonderEditorialScreenState();
|
||||||
@ -95,62 +97,71 @@ class _WonderEditorialScreenState extends State<WonderEditorialScreen> {
|
|||||||
return Opacity(opacity: opacity, child: child);
|
return Opacity(opacity: opacity, child: child);
|
||||||
},
|
},
|
||||||
// This is due to a bug: https://github.com/flutter/flutter/issues/101872
|
// This is due to a bug: https://github.com/flutter/flutter/issues/101872
|
||||||
child: RepaintBoundary(child: _TopIllustration(widget.data.type)),
|
child: RepaintBoundary(
|
||||||
|
child: _TopIllustration(
|
||||||
|
widget.data.type,
|
||||||
|
// Polish: Inject the content padding into the illustration as an offset, so it can center itself relative to the content
|
||||||
|
// this allows the background to extend underneath the vertical side nav when it has rounded corners.
|
||||||
|
fgOffset: Offset(widget.contentPadding.left / 2, 0),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// Scrolling content - Includes an invisible gap at the top, and then scrolls over the illustration
|
/// Scrolling content - Includes an invisible gap at the top, and then scrolls over the illustration
|
||||||
TopCenter(
|
TopCenter(
|
||||||
child: SizedBox(
|
child: Padding(
|
||||||
child: FocusTraversalGroup(
|
padding: widget.contentPadding,
|
||||||
child: CustomScrollView(
|
child: SizedBox(
|
||||||
primary: false,
|
child: FocusTraversalGroup(
|
||||||
controller: _scroller,
|
child: CustomScrollView(
|
||||||
scrollBehavior: ScrollConfiguration.of(context).copyWith(),
|
primary: false,
|
||||||
key: PageStorageKey('editorial'),
|
controller: _scroller,
|
||||||
slivers: [
|
scrollBehavior: ScrollConfiguration.of(context).copyWith(),
|
||||||
/// Invisible padding at the top of the list, so the illustration shows through the btm
|
key: PageStorageKey('editorial'),
|
||||||
SliverToBoxAdapter(
|
slivers: [
|
||||||
child: SizedBox(height: illustrationHeight),
|
/// Invisible padding at the top of the list, so the illustration shows through the btm
|
||||||
),
|
SliverToBoxAdapter(
|
||||||
|
child: SizedBox(height: illustrationHeight),
|
||||||
/// Text content, animates itself to hide behind the app bar as it scrolls up
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: ValueListenableBuilder<double>(
|
|
||||||
valueListenable: _scrollPos,
|
|
||||||
builder: (_, value, child) {
|
|
||||||
double offsetAmt = max(0, value * .3);
|
|
||||||
double opacity = (1 - offsetAmt / 150).clamp(0, 1);
|
|
||||||
return Transform.translate(
|
|
||||||
offset: Offset(0, offsetAmt),
|
|
||||||
child: Opacity(opacity: opacity, child: child),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: _TitleText(widget.data, scroller: _scroller),
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
/// Collapsing App bar, pins to the top of the list
|
/// Text content, animates itself to hide behind the app bar as it scrolls up
|
||||||
SliverAppBar(
|
SliverToBoxAdapter(
|
||||||
pinned: true,
|
child: ValueListenableBuilder<double>(
|
||||||
collapsedHeight: minAppBarHeight,
|
valueListenable: _scrollPos,
|
||||||
toolbarHeight: minAppBarHeight,
|
builder: (_, value, child) {
|
||||||
expandedHeight: maxAppBarHeight,
|
double offsetAmt = max(0, value * .3);
|
||||||
backgroundColor: Colors.transparent,
|
double opacity = (1 - offsetAmt / 150).clamp(0, 1);
|
||||||
elevation: 0,
|
return Transform.translate(
|
||||||
leading: SizedBox.shrink(),
|
offset: Offset(0, offsetAmt),
|
||||||
flexibleSpace: SizedBox.expand(
|
child: Opacity(opacity: opacity, child: child),
|
||||||
child: _AppBar(
|
);
|
||||||
widget.data.type,
|
},
|
||||||
scrollPos: _scrollPos,
|
child: _TitleText(widget.data, scroller: _scroller),
|
||||||
sectionIndex: _sectionIndex,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
/// Editorial content (text and images)
|
/// Collapsing App bar, pins to the top of the list
|
||||||
_ScrollingContent(widget.data, scrollPos: _scrollPos, sectionNotifier: _sectionIndex),
|
SliverAppBar(
|
||||||
],
|
pinned: true,
|
||||||
|
collapsedHeight: minAppBarHeight,
|
||||||
|
toolbarHeight: minAppBarHeight,
|
||||||
|
expandedHeight: maxAppBarHeight,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
leading: SizedBox.shrink(),
|
||||||
|
flexibleSpace: SizedBox.expand(
|
||||||
|
child: _AppBar(
|
||||||
|
widget.data.type,
|
||||||
|
scrollPos: _scrollPos,
|
||||||
|
sectionIndex: _sectionIndex,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// Editorial content (text and images)
|
||||||
|
_ScrollingContent(widget.data, scrollPos: _scrollPos, sectionNotifier: _sectionIndex),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
part of '../editorial_screen.dart';
|
part of '../editorial_screen.dart';
|
||||||
|
|
||||||
class _TopIllustration extends StatelessWidget {
|
class _TopIllustration extends StatelessWidget {
|
||||||
const _TopIllustration(this.type, {Key? key}) : super(key: key);
|
const _TopIllustration(this.type, {Key? key, this.fgOffset = Offset.zero}) : super(key: key);
|
||||||
final WonderType type;
|
final WonderType type;
|
||||||
|
final Offset fgOffset;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -11,7 +12,7 @@ class _TopIllustration extends StatelessWidget {
|
|||||||
WonderIllustration(type, config: WonderIllustrationConfig.bg(enableAnims: false, shortMode: true)),
|
WonderIllustration(type, config: WonderIllustrationConfig.bg(enableAnims: false, shortMode: true)),
|
||||||
Transform.translate(
|
Transform.translate(
|
||||||
// Small bump down to make sure we cover the edge between the editorial page and the sky.
|
// Small bump down to make sure we cover the edge between the editorial page and the sky.
|
||||||
offset: Offset(0, 10),
|
offset: fgOffset + Offset(0, 10),
|
||||||
child: WonderIllustration(
|
child: WonderIllustration(
|
||||||
type,
|
type,
|
||||||
config: WonderIllustrationConfig.mg(enableAnims: false, shortMode: true),
|
config: WonderIllustrationConfig.mg(enableAnims: false, shortMode: true),
|
||||||
|
@ -36,7 +36,12 @@ class WonderDetailsTabMenu extends StatelessWidget {
|
|||||||
top: isVertical ? context.mq.viewPadding.top : buttonInset,
|
top: isVertical ? context.mq.viewPadding.top : buttonInset,
|
||||||
right: isVertical ? buttonInset : 0,
|
right: isVertical ? buttonInset : 0,
|
||||||
),
|
),
|
||||||
child: ColoredBox(color: $styles.colors.offWhite),
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: $styles.colors.offWhite,
|
||||||
|
borderRadius: isVertical ? BorderRadius.only(topRight: Radius.circular(32)) : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -57,7 +57,6 @@ class _WonderDetailsScreenState extends State<WonderDetailsScreen>
|
|||||||
bool showTabBarBg = tabIndex != 1;
|
bool showTabBarBg = tabIndex != 1;
|
||||||
final tabBarSize = _tabBarSize ?? 0;
|
final tabBarSize = _tabBarSize ?? 0;
|
||||||
final menuPadding = _useNavRail ? EdgeInsets.only(left: tabBarSize) : EdgeInsets.only(bottom: tabBarSize);
|
final menuPadding = _useNavRail ? EdgeInsets.only(left: tabBarSize) : EdgeInsets.only(bottom: tabBarSize);
|
||||||
|
|
||||||
return ColoredBox(
|
return ColoredBox(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@ -66,19 +65,10 @@ class _WonderDetailsScreenState extends State<WonderDetailsScreen>
|
|||||||
LazyIndexedStack(
|
LazyIndexedStack(
|
||||||
index: _tabController.index,
|
index: _tabController.index,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
WonderEditorialScreen(wonder, contentPadding: menuPadding, onScroll: _handleDetailsScrolled),
|
||||||
padding: menuPadding,
|
|
||||||
child: WonderEditorialScreen(wonder, onScroll: _handleDetailsScrolled),
|
|
||||||
),
|
|
||||||
PhotoGallery(collectionId: wonder.unsplashCollectionId, wonderType: wonder.type),
|
PhotoGallery(collectionId: wonder.unsplashCollectionId, wonderType: wonder.type),
|
||||||
Padding(
|
ArtifactCarouselScreen(type: wonder.type, contentPadding: menuPadding),
|
||||||
padding: menuPadding,
|
WonderEvents(type: widget.type, contentPadding: menuPadding),
|
||||||
child: ArtifactCarouselScreen(type: wonder.type),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: menuPadding,
|
|
||||||
child: WonderEvents(type: widget.type),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ part 'widgets/_timeline_btn.dart';
|
|||||||
part 'widgets/_wonder_image_with_timeline.dart';
|
part 'widgets/_wonder_image_with_timeline.dart';
|
||||||
|
|
||||||
class WonderEvents extends StatefulWidget {
|
class WonderEvents extends StatefulWidget {
|
||||||
const WonderEvents({Key? key, required this.type}) : super(key: key);
|
const WonderEvents({Key? key, required this.type, this.contentPadding = EdgeInsets.zero}) : super(key: key);
|
||||||
final WonderType type;
|
final WonderType type;
|
||||||
|
final EdgeInsets contentPadding;
|
||||||
@override
|
@override
|
||||||
State<WonderEvents> createState() => _WonderEventsState();
|
State<WonderEvents> createState() => _WonderEventsState();
|
||||||
}
|
}
|
||||||
@ -51,7 +51,10 @@ class _WonderEventsState extends State<WonderEvents> {
|
|||||||
/// Main view
|
/// Main view
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
top: $styles.insets.sm,
|
top: $styles.insets.sm,
|
||||||
child: useTwoColumnLayout ? _buildTwoColumn(context) : _buildSingleColumn(),
|
child: Padding(
|
||||||
|
padding: widget.contentPadding,
|
||||||
|
child: useTwoColumnLayout ? _buildTwoColumn(context) : _buildSingleColumn(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
/// Header w/ TimelineBtn
|
/// Header w/ TimelineBtn
|
||||||
|
Loading…
x
Reference in New Issue
Block a user