This repository has been archived by the owner on Jun 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Keeping a few of the foldable experiments * Second screen for splash and study wrapper routes * TwoPane Demo * Dialog on screen 1 and panePriority naming * Update to latest TwoPane and foldable api * Upgrade to dual_screen 1.0.3 * Fix analyze issues * Fix formatting issues * Update code segments * Add TwoPane sample labels to l10n * Update links to actual TwoPane documentation on pub.dev * Add strings to l10n * Add demo for selecting the screen for dialogs * Make route path parameter named * Reuse code for showing the flutter logo * Move magic numbers to descriptive constants * Better description for l10n label * Update to dart 2.17 with support for super parameters * Update goldens * Remove foldable dialog type * Change texts to say small screen instead of single screen * Update goldens after copy change * Change shrine golden tests initial route to avoid double instantiation of Shrine app
- Loading branch information
1 parent
d1a9685
commit 221023a
Showing
15 changed files
with
1,797 additions
and
80 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,229 @@ | ||
// 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 'dart:ui'; | ||
import 'package:dual_screen/dual_screen.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_gen/gen_l10n/gallery_localizations.dart'; | ||
|
||
// BEGIN twoPaneDemo | ||
|
||
enum TwoPaneDemoType { | ||
foldable, | ||
tablet, | ||
smallScreen, | ||
} | ||
|
||
class TwoPaneDemo extends StatefulWidget { | ||
const TwoPaneDemo({ | ||
super.key, | ||
required this.restorationId, | ||
required this.type, | ||
}); | ||
|
||
final String restorationId; | ||
final TwoPaneDemoType type; | ||
|
||
@override | ||
TwoPaneDemoState createState() => TwoPaneDemoState(); | ||
} | ||
|
||
class TwoPaneDemoState extends State<TwoPaneDemo> with RestorationMixin { | ||
final RestorableInt _currentIndex = RestorableInt(-1); | ||
|
||
@override | ||
String get restorationId => widget.restorationId; | ||
|
||
@override | ||
void restoreState(RestorationBucket? oldBucket, bool initialRestore) { | ||
registerForRestoration(_currentIndex, 'two_pane_selected_item'); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
_currentIndex.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
var panePriority = TwoPanePriority.both; | ||
if (widget.type == TwoPaneDemoType.smallScreen) { | ||
panePriority = _currentIndex.value == -1 | ||
? TwoPanePriority.start | ||
: TwoPanePriority.end; | ||
} | ||
return SimulateScreen( | ||
type: widget.type, | ||
child: TwoPane( | ||
paneProportion: 0.3, | ||
panePriority: panePriority, | ||
startPane: ListPane( | ||
selectedIndex: _currentIndex.value, | ||
onSelect: (index) { | ||
setState(() { | ||
_currentIndex.value = index; | ||
}); | ||
}, | ||
), | ||
endPane: DetailsPane( | ||
selectedIndex: _currentIndex.value, | ||
onClose: widget.type == TwoPaneDemoType.smallScreen | ||
? () { | ||
setState(() { | ||
_currentIndex.value = -1; | ||
}); | ||
} | ||
: null, | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class ListPane extends StatelessWidget { | ||
final ValueChanged<int> onSelect; | ||
final int selectedIndex; | ||
|
||
const ListPane({ | ||
super.key, | ||
required this.onSelect, | ||
required this.selectedIndex, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
automaticallyImplyLeading: false, | ||
title: Text(GalleryLocalizations.of(context)!.demoTwoPaneList), | ||
), | ||
body: Scrollbar( | ||
child: ListView( | ||
restorationId: 'list_demo_list_view', | ||
padding: const EdgeInsets.symmetric(vertical: 8), | ||
children: [ | ||
for (int index = 1; index < 21; index++) | ||
ListTile( | ||
onTap: () { | ||
onSelect(index); | ||
}, | ||
selected: selectedIndex == index, | ||
leading: ExcludeSemantics( | ||
child: CircleAvatar(child: Text('$index')), | ||
), | ||
title: Text( | ||
GalleryLocalizations.of(context)!.demoTwoPaneItem(index), | ||
), | ||
), | ||
], | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class DetailsPane extends StatelessWidget { | ||
final VoidCallback? onClose; | ||
final int selectedIndex; | ||
|
||
const DetailsPane({ | ||
super.key, | ||
required this.selectedIndex, | ||
this.onClose, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
automaticallyImplyLeading: false, | ||
leading: onClose == null | ||
? null | ||
: IconButton(icon: const Icon(Icons.close), onPressed: onClose), | ||
title: Text( | ||
GalleryLocalizations.of(context)!.demoTwoPaneDetails, | ||
), | ||
), | ||
body: Container( | ||
color: const Color(0xfffafafa), | ||
child: Center( | ||
child: Text( | ||
selectedIndex == -1 | ||
? GalleryLocalizations.of(context)!.demoTwoPaneSelectItem | ||
: GalleryLocalizations.of(context)! | ||
.demoTwoPaneItemDetails(selectedIndex), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class SimulateScreen extends StatelessWidget { | ||
const SimulateScreen({ | ||
super.key, | ||
required this.type, | ||
required this.child, | ||
}); | ||
|
||
final TwoPaneDemoType type; | ||
final TwoPane child; | ||
|
||
// An approximation of a real foldable | ||
static const double foldableAspectRatio = 20 / 18; | ||
// 16x9 candy bar phone | ||
static const double singleScreenAspectRatio = 9 / 16; | ||
// Taller desktop / tablet | ||
static const double tabletAspectRatio = 4 / 3; | ||
// How wide should the hinge be, as a proportion of total width | ||
static const double hingeProportion = 1 / 35; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Center( | ||
child: Container( | ||
decoration: BoxDecoration( | ||
color: Colors.black, | ||
borderRadius: BorderRadius.circular(16), | ||
), | ||
padding: const EdgeInsets.all(14), | ||
child: AspectRatio( | ||
aspectRatio: type == TwoPaneDemoType.foldable | ||
? foldableAspectRatio | ||
: type == TwoPaneDemoType.tablet | ||
? tabletAspectRatio | ||
: singleScreenAspectRatio, | ||
child: LayoutBuilder(builder: (context, constraints) { | ||
final size = Size(constraints.maxWidth, constraints.maxHeight); | ||
final hingeSize = Size(size.width * hingeProportion, size.height); | ||
// Position the hinge in the middle of the display | ||
final hingeBounds = Rect.fromLTWH( | ||
(size.width - hingeSize.width) / 2, | ||
0, | ||
hingeSize.width, | ||
hingeSize.height, | ||
); | ||
return MediaQuery( | ||
data: MediaQueryData( | ||
size: size, | ||
displayFeatures: [ | ||
if (type == TwoPaneDemoType.foldable) | ||
DisplayFeature( | ||
bounds: hingeBounds, | ||
type: DisplayFeatureType.hinge, | ||
state: DisplayFeatureState.postureFlat, | ||
), | ||
], | ||
), | ||
child: child, | ||
); | ||
}), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
// END |
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
Oops, something went wrong.