Commit 701e0d05 authored by Collin Jackson's avatar Collin Jackson Committed by GitHub

Add update dialog to gallery (#5569)

Add update dialog to gallery, open source part of #4626
parent 1852fdcc
...@@ -7,11 +7,12 @@ import 'package:flutter/scheduler.dart' show timeDilation; ...@@ -7,11 +7,12 @@ import 'package:flutter/scheduler.dart' show timeDilation;
import 'item.dart'; import 'item.dart';
import 'home.dart'; import 'home.dart';
import 'updates.dart';
final Map<String, WidgetBuilder> _kRoutes = new Map<String, WidgetBuilder>.fromIterable( final Map<String, WidgetBuilder> _kRoutes = new Map<String, WidgetBuilder>.fromIterable(
kAllGalleryItems, kAllGalleryItems,
key: (GalleryItem item) => item.routeName, key: (GalleryItem item) => item.routeName,
value: (GalleryItem item) => item.buildRoute value: (GalleryItem item) => item.buildRoute,
); );
final ThemeData _kGalleryLightTheme = new ThemeData( final ThemeData _kGalleryLightTheme = new ThemeData(
...@@ -21,11 +22,13 @@ final ThemeData _kGalleryLightTheme = new ThemeData( ...@@ -21,11 +22,13 @@ final ThemeData _kGalleryLightTheme = new ThemeData(
final ThemeData _kGalleryDarkTheme = new ThemeData( final ThemeData _kGalleryDarkTheme = new ThemeData(
brightness: Brightness.dark, brightness: Brightness.dark,
primarySwatch: Colors.lightBlue primarySwatch: Colors.lightBlue,
); );
class GalleryApp extends StatefulWidget { class GalleryApp extends StatefulWidget {
GalleryApp({ Key key }) : super(key: key); GalleryApp({this.updateUrlFetcher, Key key}) : super(key: key);
final UpdateUrlFetcher updateUrlFetcher;
@override @override
GalleryAppState createState() => new GalleryAppState(); GalleryAppState createState() => new GalleryAppState();
...@@ -47,27 +50,44 @@ class GalleryAppState extends State<GalleryApp> { ...@@ -47,27 +50,44 @@ class GalleryAppState extends State<GalleryApp> {
Widget home = new GalleryHome( Widget home = new GalleryHome(
useLightTheme: _useLightTheme, useLightTheme: _useLightTheme,
onThemeChanged: (bool value) { setState(() { _useLightTheme = value; }); }, onThemeChanged: (bool value) {
setState(() {
_useLightTheme = value;
});
},
showPerformanceOverlay: _showPerformanceOverlay, showPerformanceOverlay: _showPerformanceOverlay,
onShowPerformanceOverlayChanged: (bool value) { setState(() { _showPerformanceOverlay = value; }); }, onShowPerformanceOverlayChanged: (bool value) {
setState(() {
_showPerformanceOverlay = value;
});
},
timeDilation: timeDilation, timeDilation: timeDilation,
onTimeDilationChanged: (double value) { setState(() { timeDilation = value; }); } onTimeDilationChanged: (double value) {
setState(() {
timeDilation = value;
});
},
); );
if (showPreviewBanner) { if (showPreviewBanner)
home = new Banner( home = new Banner(
message: 'PREVIEW', message: 'PREVIEW',
location: BannerLocation.topRight, location: BannerLocation.topRight,
child: home child: home,
);
if (config.updateUrlFetcher != null)
home = new Updater(
updateUrlFetcher: config.updateUrlFetcher,
child: home,
); );
}
return new MaterialApp( return new MaterialApp(
title: 'Flutter Gallery', title: 'Flutter Gallery',
theme: _useLightTheme ? _kGalleryLightTheme : _kGalleryDarkTheme, theme: _useLightTheme ? _kGalleryLightTheme : _kGalleryDarkTheme,
showPerformanceOverlay: _showPerformanceOverlay, showPerformanceOverlay: _showPerformanceOverlay,
routes: _kRoutes, routes: _kRoutes,
home: home home: home,
); );
} }
} }
// Copyright 2016 The Chromium Authors. 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:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
typedef Future<String> UpdateUrlFetcher();
class Updater extends StatefulWidget {
Updater({ this.updateUrlFetcher, this.child, Key key }) : super(key: key) {
assert(updateUrlFetcher != null);
}
final UpdateUrlFetcher updateUrlFetcher;
final Widget child;
@override
State createState() => new UpdaterState();
}
class UpdaterState extends State<Updater> {
@override
void initState() {
super.initState();
_checkForUpdates();
}
static DateTime _lastUpdateCheck;
Future<Null> _checkForUpdates() async {
// Only prompt once a day
if (_lastUpdateCheck != null &&
new DateTime.now().difference(_lastUpdateCheck) < new Duration(days: 1)) {
return; // We already checked for updates recently
}
_lastUpdateCheck = new DateTime.now();
String updateUrl = await config.updateUrlFetcher();
if (updateUrl != null) {
bool wantsUpdate = await showDialog(context: context, child: _buildDialog());
if (wantsUpdate != null && wantsUpdate)
UrlLauncher.launch(updateUrl);
}
}
Widget _buildDialog() {
final ThemeData theme = Theme.of(context);
final TextStyle dialogTextStyle =
theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color);
return new Dialog(
title: new Text('Update Flutter Gallery?'),
content: new Text('A newer version is available.', style: dialogTextStyle),
actions: <Widget>[
new FlatButton(
child: new Text('NO THANKS'),
onPressed: () {
Navigator.pop(context, false);
}),
new FlatButton(
child: new Text('UPDATE'),
onPressed: () {
Navigator.pop(context, true);
}),
]);
}
@override
Widget build(BuildContext context) => config.child;
}
// Copyright 2016 The Chromium Authors. 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';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_gallery/gallery/app.dart';
Future<String> mockUpdateUrlFetcher() {
// A real implementation would connect to the network to retrieve this value
return new Future<String>.value('http://www.example.com/');
}
Finder byTooltip(WidgetTester tester, String message) {
return find.byWidgetPredicate((Widget widget) {
return widget is Tooltip && widget.message == message;
});
}
Finder findBackButton(WidgetTester tester) => byTooltip(tester, 'Back');
void main() {
TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding) binding.allowAllFrames = true;
// Regression test for https://github.com/flutter/flutter/pull/5168
testWidgets('update dialog', (WidgetTester tester) async {
await tester.pumpWidget(new GalleryApp(updateUrlFetcher: mockUpdateUrlFetcher));
await tester.pump(); // see https://github.com/flutter/flutter/issues/1865
await tester.pump(); // triggers a frame
expect(find.text('UPDATE'), findsOneWidget);
await tester.tap(find.text('NO THANKS'));
await tester.pump();
await tester.tap(find.text('Shrine'));
await tester.pump(); // Launch shrine
await tester.pump(const Duration(seconds: 1)); // transition is complete
Finder backButton = findBackButton(tester);
expect(backButton, findsOneWidget);
await tester.tap(backButton);
await tester.pump(); // Start the pop "back" operation.
await tester.pump(const Duration(seconds: 1)); // transition is complete
expect(find.text('UPDATE'), findsNothing);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment