Skip to content

Commit 43a8b6a

Browse files
Reverts "Remove dual_screen from new_gallery integration test (flutter#150808)" (flutter#150871)
Reverts: flutter#150808 Initiated by: gmackall Reason for reverting: Causing the new_gallery tests to hang. I can repro now though, so should be able to find a fix shortly Original PR Author: gmackall Reviewed By: {christopherfujino, johnmccutchan, jtmcdole, jonahwilliams} This change reverts the following previous change: Removes the `dual_screen` package from `new_gallery`. Unblocks the fourth attempt to land flutter/engine#53001.
1 parent 1d13676 commit 43a8b6a

File tree

9 files changed

+442
-24
lines changed

9 files changed

+442
-24
lines changed

dev/integration_tests/new_gallery/lib/data/demos.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import '../demos/reference/motion_demo_shared_y_axis_transition.dart';
2626
import '../demos/reference/motion_demo_shared_z_axis_transition.dart';
2727
import '../demos/reference/transformations_demo.dart'
2828
deferred as transformations_demo;
29+
import '../demos/reference/two_pane_demo.dart'
30+
deferred as twopane_demo;
2931
import '../demos/reference/typography_demo.dart'
3032
deferred as typography;
3133
import '../gallery_localizations.dart';
@@ -1156,6 +1158,54 @@ class Demos {
11561158

11571159
static List<GalleryDemo> otherDemos(GalleryLocalizations localizations) {
11581160
return <GalleryDemo>[
1161+
GalleryDemo(
1162+
title: localizations.demoTwoPaneTitle,
1163+
icon: GalleryIcons.bottomSheetPersistent,
1164+
slug: 'two-pane',
1165+
subtitle: localizations.demoTwoPaneSubtitle,
1166+
configurations: <GalleryDemoConfiguration>[
1167+
GalleryDemoConfiguration(
1168+
title: localizations.demoTwoPaneFoldableLabel,
1169+
description: localizations.demoTwoPaneFoldableDescription,
1170+
documentationUrl:
1171+
'https://pub.dev/documentation/dual_screen/latest/dual_screen/TwoPane-class.html',
1172+
buildRoute: (_) => DeferredWidget(
1173+
twopane_demo.loadLibrary,
1174+
() => twopane_demo.TwoPaneDemo(
1175+
type: twopane_demo.TwoPaneDemoType.foldable,
1176+
restorationId: 'two_pane_foldable',
1177+
),
1178+
),
1179+
),
1180+
GalleryDemoConfiguration(
1181+
title: localizations.demoTwoPaneTabletLabel,
1182+
description: localizations.demoTwoPaneTabletDescription,
1183+
documentationUrl:
1184+
'https://pub.dev/documentation/dual_screen/latest/dual_screen/TwoPane-class.html',
1185+
buildRoute: (_) => DeferredWidget(
1186+
twopane_demo.loadLibrary,
1187+
() => twopane_demo.TwoPaneDemo(
1188+
type: twopane_demo.TwoPaneDemoType.tablet,
1189+
restorationId: 'two_pane_tablet',
1190+
),
1191+
),
1192+
),
1193+
GalleryDemoConfiguration(
1194+
title: localizations.demoTwoPaneSmallScreenLabel,
1195+
description: localizations.demoTwoPaneSmallScreenDescription,
1196+
documentationUrl:
1197+
'https://pub.dev/documentation/dual_screen/latest/dual_screen/TwoPane-class.html',
1198+
buildRoute: (_) => DeferredWidget(
1199+
twopane_demo.loadLibrary,
1200+
() => twopane_demo.TwoPaneDemo(
1201+
type: twopane_demo.TwoPaneDemoType.smallScreen,
1202+
restorationId: 'two_pane_single',
1203+
),
1204+
),
1205+
),
1206+
],
1207+
category: GalleryDemoCategory.other,
1208+
),
11591209
GalleryDemo(
11601210
title: localizations.demoMotionTitle,
11611211
icon: GalleryIcons.animation,
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:ui';
6+
import 'package:dual_screen/dual_screen.dart';
7+
import 'package:flutter/material.dart';
8+
import '../../gallery_localizations.dart';
9+
10+
// BEGIN twoPaneDemo
11+
12+
enum TwoPaneDemoType {
13+
foldable,
14+
tablet,
15+
smallScreen,
16+
}
17+
18+
class TwoPaneDemo extends StatefulWidget {
19+
const TwoPaneDemo({
20+
super.key,
21+
required this.restorationId,
22+
required this.type,
23+
});
24+
25+
final String restorationId;
26+
final TwoPaneDemoType type;
27+
28+
@override
29+
TwoPaneDemoState createState() => TwoPaneDemoState();
30+
}
31+
32+
class TwoPaneDemoState extends State<TwoPaneDemo> with RestorationMixin {
33+
final RestorableInt _currentIndex = RestorableInt(-1);
34+
35+
@override
36+
String get restorationId => widget.restorationId;
37+
38+
@override
39+
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
40+
registerForRestoration(_currentIndex, 'two_pane_selected_item');
41+
}
42+
43+
@override
44+
void dispose() {
45+
_currentIndex.dispose();
46+
super.dispose();
47+
}
48+
49+
@override
50+
Widget build(BuildContext context) {
51+
TwoPanePriority panePriority = TwoPanePriority.both;
52+
if (widget.type == TwoPaneDemoType.smallScreen) {
53+
panePriority = _currentIndex.value == -1
54+
? TwoPanePriority.start
55+
: TwoPanePriority.end;
56+
}
57+
return SimulateScreen(
58+
type: widget.type,
59+
child: TwoPane(
60+
paneProportion: 0.3,
61+
panePriority: panePriority,
62+
startPane: ListPane(
63+
selectedIndex: _currentIndex.value,
64+
onSelect: (int index) {
65+
setState(() {
66+
_currentIndex.value = index;
67+
});
68+
},
69+
),
70+
endPane: DetailsPane(
71+
selectedIndex: _currentIndex.value,
72+
onClose: switch (widget.type) {
73+
TwoPaneDemoType.smallScreen => () => setState(() { _currentIndex.value = -1; }),
74+
TwoPaneDemoType.foldable || TwoPaneDemoType.tablet => null,
75+
},
76+
),
77+
),
78+
);
79+
}
80+
}
81+
82+
class ListPane extends StatelessWidget {
83+
84+
const ListPane({
85+
super.key,
86+
required this.onSelect,
87+
required this.selectedIndex,
88+
});
89+
final ValueChanged<int> onSelect;
90+
final int selectedIndex;
91+
92+
@override
93+
Widget build(BuildContext context) {
94+
return Scaffold(
95+
appBar: AppBar(
96+
automaticallyImplyLeading: false,
97+
title: Text(GalleryLocalizations.of(context)!.demoTwoPaneList),
98+
),
99+
body: Scrollbar(
100+
child: ListView(
101+
restorationId: 'list_demo_list_view',
102+
padding: const EdgeInsets.symmetric(vertical: 8),
103+
children: <Widget>[
104+
for (int index = 1; index < 21; index++)
105+
ListTile(
106+
onTap: () {
107+
onSelect(index);
108+
},
109+
selected: selectedIndex == index,
110+
leading: ExcludeSemantics(
111+
child: CircleAvatar(child: Text('$index')),
112+
),
113+
title: Text(
114+
GalleryLocalizations.of(context)!.demoTwoPaneItem(index),
115+
),
116+
),
117+
],
118+
),
119+
),
120+
);
121+
}
122+
}
123+
124+
class DetailsPane extends StatelessWidget {
125+
126+
const DetailsPane({
127+
super.key,
128+
required this.selectedIndex,
129+
this.onClose,
130+
});
131+
final VoidCallback? onClose;
132+
final int selectedIndex;
133+
134+
@override
135+
Widget build(BuildContext context) {
136+
return Scaffold(
137+
appBar: AppBar(
138+
automaticallyImplyLeading: false,
139+
leading: onClose == null
140+
? null
141+
: IconButton(icon: const Icon(Icons.close), onPressed: onClose),
142+
title: Text(
143+
GalleryLocalizations.of(context)!.demoTwoPaneDetails,
144+
),
145+
),
146+
body: ColoredBox(
147+
color: const Color(0xfffafafa),
148+
child: Center(
149+
child: Text(
150+
selectedIndex == -1
151+
? GalleryLocalizations.of(context)!.demoTwoPaneSelectItem
152+
: GalleryLocalizations.of(context)!
153+
.demoTwoPaneItemDetails(selectedIndex),
154+
),
155+
),
156+
),
157+
);
158+
}
159+
}
160+
161+
class SimulateScreen extends StatelessWidget {
162+
const SimulateScreen({
163+
super.key,
164+
required this.type,
165+
required this.child,
166+
});
167+
168+
final TwoPaneDemoType type;
169+
final TwoPane child;
170+
171+
// An approximation of a real foldable
172+
static const double foldableAspectRatio = 20 / 18;
173+
// 16x9 candy bar phone
174+
static const double singleScreenAspectRatio = 9 / 16;
175+
// Taller desktop / tablet
176+
static const double tabletAspectRatio = 4 / 3;
177+
// How wide should the hinge be, as a proportion of total width
178+
static const double hingeProportion = 1 / 35;
179+
180+
@override
181+
Widget build(BuildContext context) {
182+
return Center(
183+
child: Container(
184+
decoration: BoxDecoration(
185+
color: Colors.black,
186+
borderRadius: BorderRadius.circular(16),
187+
),
188+
padding: const EdgeInsets.all(14),
189+
child: AspectRatio(
190+
aspectRatio: switch (type) {
191+
TwoPaneDemoType.foldable => foldableAspectRatio,
192+
TwoPaneDemoType.tablet => tabletAspectRatio,
193+
TwoPaneDemoType.smallScreen => singleScreenAspectRatio,
194+
},
195+
child: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
196+
final Size size = Size(constraints.maxWidth, constraints.maxHeight);
197+
final Size hingeSize = Size(size.width * hingeProportion, size.height);
198+
// Position the hinge in the middle of the display
199+
final Rect hingeBounds = Rect.fromLTWH(
200+
(size.width - hingeSize.width) / 2,
201+
0,
202+
hingeSize.width,
203+
hingeSize.height,
204+
);
205+
return MediaQuery(
206+
data: MediaQueryData(
207+
size: size,
208+
displayFeatures: <DisplayFeature>[
209+
if (type == TwoPaneDemoType.foldable)
210+
DisplayFeature(
211+
bounds: hingeBounds,
212+
type: DisplayFeatureType.hinge,
213+
state: DisplayFeatureState.postureFlat,
214+
),
215+
],
216+
),
217+
child: child,
218+
);
219+
}),
220+
),
221+
),
222+
);
223+
}
224+
}
225+
226+
// END

dev/integration_tests/new_gallery/lib/layout/adaptive.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:ui';
6+
57
import 'package:adaptive_breakpoints/adaptive_breakpoints.dart';
8+
import 'package:dual_screen/dual_screen.dart';
69
import 'package:flutter/material.dart';
710

811
/// The maximum width taken up by each item on the home screen.
912
const double maxHomeItemWidth = 1400.0;
1013

1114
/// Returns a boolean value whether the window is considered medium or large size.
1215
///
13-
/// Widgets using this method might consider the display is
16+
/// When running on a desktop device that is also foldable, the display is not
17+
/// considered desktop. Widgets using this method might consider the display is
1418
/// large enough for certain layouts, which is not the case on foldable devices,
1519
/// where only part of the display is available to said widgets.
1620
///
1721
/// Used to build adaptive and responsive layouts.
1822
bool isDisplayDesktop(BuildContext context) =>
23+
!isDisplayFoldable(context) &&
1924
getWindowType(context) >= AdaptiveWindowType.medium;
2025

2126
/// Returns boolean value whether the window is considered medium size.
@@ -24,3 +29,16 @@ bool isDisplayDesktop(BuildContext context) =>
2429
bool isDisplaySmallDesktop(BuildContext context) {
2530
return getWindowType(context) == AdaptiveWindowType.medium;
2631
}
32+
33+
/// Returns a boolean value whether the display has a hinge that splits the
34+
/// screen into two, left and right sub-screens. Horizontal splits (top and
35+
/// bottom sub-screens) are ignored for this application.
36+
bool isDisplayFoldable(BuildContext context) {
37+
final DisplayFeature? hinge = MediaQuery.of(context).hinge;
38+
if (hinge == null) {
39+
return false;
40+
} else {
41+
// Vertical
42+
return hinge.bounds.size.aspectRatio < 1;
43+
}
44+
}

dev/integration_tests/new_gallery/lib/main.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:dual_screen/dual_screen.dart';
56
import 'package:flutter/foundation.dart';
67
import 'package:flutter/material.dart';
78
import 'package:flutter/scheduler.dart' show timeDilation;
@@ -49,6 +50,7 @@ class GalleryApp extends StatelessWidget {
4950
child: Builder(
5051
builder: (BuildContext context) {
5152
final GalleryOptions options = GalleryOptions.of(context);
53+
final bool hasHinge = MediaQuery.of(context).hinge?.bounds != null;
5254
return MaterialApp(
5355
restorationScopeId: 'rootGallery',
5456
title: 'Flutter Gallery',
@@ -72,7 +74,7 @@ class GalleryApp extends StatelessWidget {
7274
return basicLocaleListResolution(locales, supportedLocales);
7375
},
7476
onGenerateRoute: (RouteSettings settings) =>
75-
RouteConfiguration.onGenerateRoute(settings),
77+
RouteConfiguration.onGenerateRoute(settings, hasHinge),
7678
);
7779
},
7880
),

0 commit comments

Comments
 (0)