[Gallery] Crane focus (#254)

* Add debugLabel to HighlightFocus

* Remove decrated property

* Add focus to DestinationCards

* Add index property to Forms

* Move BackLayer stuff to its own file

* Add focus

* Revert deprecated change

* Address feedback
pull/257/head
Pierre-Louis 5 years ago committed by GitHub
parent a89c9a4747
commit 7f783273c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -15,6 +15,7 @@ class HighlightFocus extends StatefulWidget {
this.highlightColor,
this.borderColor,
this.hasFocus = true,
this.debugLabel,
});
/// [onPressed] is called when you press space, enter, or numpad-enter
@ -37,6 +38,8 @@ class HighlightFocus extends StatefulWidget {
/// Set to false if you want the child to skip focus.
final bool hasFocus;
final String debugLabel;
@override
_HighlightFocusState createState() => _HighlightFocusState();
}
@ -67,6 +70,7 @@ class _HighlightFocusState extends State<HighlightFocus> {
return Focus(
canRequestFocus: widget.hasFocus,
debugLabel: widget.debugLabel,
onFocusChange: (newValue) {
setState(() {
isFocused = newValue;

@ -41,10 +41,10 @@ class _CraneAppState extends State<CraneApp> {
home: ApplyTextOptions(
child: Backdrop(
frontLayer: Container(),
backLayer: [
FlyForm(),
SleepForm(),
EatForm(),
backLayerItems: [
FlyForm(index: 0),
SleepForm(index: 1),
EatForm(index: 2),
],
frontTitle: Text('CRANE'),
backTitle: Text('MENU'),

@ -13,6 +13,7 @@ import 'package:gallery/data/gallery_options.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
import 'package:gallery/layout/adaptive.dart';
import 'package:gallery/studies/crane/border_tab_indicator.dart';
import 'package:gallery/studies/crane/backlayer.dart';
import 'package:gallery/studies/crane/colors.dart';
import 'package:gallery/studies/crane/item_cards.dart';
@ -32,26 +33,29 @@ class _FrontLayer extends StatelessWidget {
Widget build(BuildContext context) {
final isDesktop = isDisplayDesktop(context);
return PhysicalShape(
elevation: 16,
color: cranePrimaryWhite,
clipper: ShapeBorderClipper(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(frontLayerBorderRadius),
topRight: Radius.circular(frontLayerBorderRadius),
return DefaultFocusTraversal(
policy: ReadingOrderTraversalPolicy(),
child: PhysicalShape(
elevation: 16,
color: cranePrimaryWhite,
clipper: ShapeBorderClipper(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(frontLayerBorderRadius),
topRight: Radius.circular(frontLayerBorderRadius),
),
),
),
),
child: ListView(
padding: isDesktop
? EdgeInsets.symmetric(horizontal: 120, vertical: 22)
: EdgeInsets.all(20),
children: [
Text(title, style: Theme.of(context).textTheme.subtitle),
SizedBox(height: 20),
ItemCards(index: index),
],
child: ListView(
padding: isDesktop
? EdgeInsets.symmetric(horizontal: 120, vertical: 22)
: EdgeInsets.all(20),
children: [
Text(title, style: Theme.of(context).textTheme.subtitle),
SizedBox(height: 20),
ItemCards(index: index),
],
),
),
);
}
@ -65,17 +69,17 @@ class _FrontLayer extends StatelessWidget {
/// front or back layer is showing.
class Backdrop extends StatefulWidget {
final Widget frontLayer;
final List<Widget> backLayer;
final List<BackLayerItem> backLayerItems;
final Widget frontTitle;
final Widget backTitle;
const Backdrop({
@required this.frontLayer,
@required this.backLayer,
@required this.backLayerItems,
@required this.frontTitle,
@required this.backTitle,
}) : assert(frontLayer != null),
assert(backLayer != null),
assert(backLayerItems != null),
assert(frontTitle != null),
assert(backTitle != null);
@ -125,61 +129,68 @@ class _BackdropState extends State<Backdrop> with TickerProviderStateMixin {
color: cranePurple800,
child: Padding(
padding: EdgeInsets.only(top: 12),
child: Scaffold(
backgroundColor: cranePurple800,
appBar: AppBar(
brightness: Brightness.dark,
elevation: 0,
titleSpacing: 0,
flexibleSpace: CraneAppBar(
tabController: _tabController,
tabHandler: _handleTabs,
),
),
body: Stack(
children: [
BackLayer(
child: DefaultFocusTraversal(
policy: ReadingOrderTraversalPolicy(),
child: Scaffold(
backgroundColor: cranePurple800,
appBar: AppBar(
brightness: Brightness.dark,
elevation: 0,
titleSpacing: 0,
flexibleSpace: CraneAppBar(
tabController: _tabController,
backLayers: widget.backLayer,
tabHandler: _handleTabs,
),
Container(
margin: EdgeInsets.only(
top: isDesktop
? 60 + 20 * textScaleFactor / 2
: 175 + 140 * textScaleFactor / 2,
),
child: TabBarView(
physics: isDesktop
? NeverScrollableScrollPhysics()
: null, // use default TabBarView physics
controller: _tabController,
children: [
SlideTransition(
position: _flyLayerOffset,
child: _FrontLayer(
title: GalleryLocalizations.of(context).craneFlySubhead,
index: 0,
),
),
SlideTransition(
position: _sleepLayerOffset,
child: _FrontLayer(
title:
GalleryLocalizations.of(context).craneSleepSubhead,
index: 1,
),
),
body: FocusScope(
child: Stack(
children: [
BackLayer(
tabController: _tabController,
backLayerItems: widget.backLayerItems,
),
Container(
margin: EdgeInsets.only(
top: isDesktop
? 60 + 20 * textScaleFactor / 2
: 175 + 140 * textScaleFactor / 2,
),
SlideTransition(
position: _eatLayerOffset,
child: _FrontLayer(
title: GalleryLocalizations.of(context).craneEatSubhead,
index: 2,
),
child: TabBarView(
physics: isDesktop
? NeverScrollableScrollPhysics()
: null, // use default TabBarView physics
controller: _tabController,
children: [
SlideTransition(
position: _flyLayerOffset,
child: _FrontLayer(
title: GalleryLocalizations.of(context)
.craneFlySubhead,
index: 0,
),
),
SlideTransition(
position: _sleepLayerOffset,
child: _FrontLayer(
title: GalleryLocalizations.of(context)
.craneSleepSubhead,
index: 1,
),
),
SlideTransition(
position: _eatLayerOffset,
child: _FrontLayer(
title: GalleryLocalizations.of(context)
.craneEatSubhead,
index: 2,
),
),
],
),
],
),
),
],
),
],
),
),
),
),
@ -187,33 +198,6 @@ class _BackdropState extends State<Backdrop> with TickerProviderStateMixin {
}
}
class BackLayer extends StatefulWidget {
final List<Widget> backLayers;
final TabController tabController;
const BackLayer({Key key, this.backLayers, this.tabController})
: super(key: key);
@override
_BackLayerState createState() => _BackLayerState();
}
class _BackLayerState extends State<BackLayer> {
@override
void initState() {
super.initState();
widget.tabController.addListener(() => setState(() {}));
}
@override
Widget build(BuildContext context) {
return IndexedStack(
index: widget.tabController.index,
children: widget.backLayers,
);
}
}
class CraneAppBar extends StatefulWidget {
final Function(int) tabHandler;
final TabController tabController;

@ -0,0 +1,49 @@
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
abstract class BackLayerItem extends StatefulWidget {
final int index;
BackLayerItem({Key key, this.index}) : super(key: key);
}
class BackLayer extends StatefulWidget {
final List<BackLayerItem> backLayerItems;
final TabController tabController;
const BackLayer({Key key, this.backLayerItems, this.tabController})
: super(key: key);
@override
_BackLayerState createState() => _BackLayerState();
}
class _BackLayerState extends State<BackLayer> {
@override
void initState() {
super.initState();
widget.tabController.addListener(() => setState(() {}));
}
@override
Widget build(BuildContext context) {
final tabIndex = widget.tabController.index;
return DefaultFocusTraversal(
policy: WidgetOrderFocusTraversalPolicy(),
child: IndexedStack(
index: tabIndex,
children: [
for (BackLayerItem backLayerItem in widget.backLayerItems)
Focus(
canRequestFocus: backLayerItem.index == tabIndex,
debugLabel: 'backLayerItem: $backLayerItem',
child: backLayerItem,
)
],
),
);
}
}

@ -5,9 +5,12 @@
import 'package:flutter/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
import 'package:gallery/studies/crane/backlayer.dart';
import 'package:gallery/studies/crane/header_form.dart';
class EatForm extends StatefulWidget {
class EatForm extends BackLayerItem {
EatForm({int index}) : super(index: index);
@override
_EatFormState createState() => _EatFormState();
}

@ -5,9 +5,12 @@
import 'package:flutter/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
import 'package:gallery/studies/crane/backlayer.dart';
import 'package:gallery/studies/crane/header_form.dart';
class FlyForm extends StatefulWidget {
class FlyForm extends BackLayerItem {
FlyForm({int index}) : super(index: index);
@override
_FlyFormState createState() => _FlyFormState();
}

@ -5,6 +5,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:gallery/layout/adaptive.dart';
import 'package:gallery/layout/highlight_focus.dart';
import 'package:gallery/studies/crane/model/data.dart';
import 'package:gallery/studies/crane/model/destination.dart';
@ -33,8 +34,13 @@ class _ItemCardsState extends State<ItemCards> {
return destinations
.map(
(d) => RepaintBoundary(
child: _DestinationCard(destination: d),
(d) => HighlightFocus(
debugLabel: 'DestinationCard: ${d.destination}',
highlightColor: Colors.red.withOpacity(0.5),
onPressed: () {},
child: RepaintBoundary(
child: _DestinationCard(destination: d),
),
),
)
.toList();

@ -5,9 +5,12 @@
import 'package:flutter/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
import 'package:gallery/studies/crane/backlayer.dart';
import 'package:gallery/studies/crane/header_form.dart';
class SleepForm extends StatefulWidget {
class SleepForm extends BackLayerItem {
SleepForm({int index}) : super(index: index);
@override
_SleepFormState createState() => _SleepFormState();
}

Loading…
Cancel
Save