blob: 8d4fd9667d6c530a982d3e09bb483b61de6d4e18 [file] [log] [blame]
// 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/foundation.dart';
import 'package:flutter/material.dart';
import 'drawer.dart';
import 'item.dart';
import 'theme.dart';
const double _kFlexibleSpaceMaxHeight = 256.0;
const String _kGalleryAssetsPackage = 'flutter_gallery_assets';
class _BackgroundLayer {
_BackgroundLayer({ int level, double parallax })
: assetName = 'appbar/appbar_background_layer$level.png',
assetPackage = _kGalleryAssetsPackage,
parallaxTween = new Tween<double>(begin: 0.0, end: parallax);
final String assetName;
final String assetPackage;
final Tween<double> parallaxTween;
}
final List<_BackgroundLayer> _kBackgroundLayers = <_BackgroundLayer>[
new _BackgroundLayer(level: 0, parallax: _kFlexibleSpaceMaxHeight),
new _BackgroundLayer(level: 1, parallax: _kFlexibleSpaceMaxHeight),
new _BackgroundLayer(level: 2, parallax: _kFlexibleSpaceMaxHeight / 2.0),
new _BackgroundLayer(level: 3, parallax: _kFlexibleSpaceMaxHeight / 4.0),
new _BackgroundLayer(level: 4, parallax: _kFlexibleSpaceMaxHeight / 2.0),
new _BackgroundLayer(level: 5, parallax: _kFlexibleSpaceMaxHeight)
];
class _AppBarBackground extends StatelessWidget {
const _AppBarBackground({ Key key, this.animation }) : super(key: key);
final Animation<double> animation;
@override
Widget build(BuildContext context) {
return new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return new Stack(
children: _kBackgroundLayers.map((_BackgroundLayer layer) {
return new Positioned(
top: -layer.parallaxTween.evaluate(animation),
left: 0.0,
right: 0.0,
bottom: 0.0,
child: new Image.asset(
layer.assetName,
package: layer.assetPackage,
fit: BoxFit.cover,
height: _kFlexibleSpaceMaxHeight
)
);
}).toList()
);
}
);
}
}
class GalleryHome extends StatefulWidget {
const GalleryHome({
Key key,
this.galleryTheme,
@required this.onThemeChanged,
this.timeDilation,
@required this.onTimeDilationChanged,
this.textScaleFactor,
this.onTextScaleFactorChanged,
this.showPerformanceOverlay,
this.onShowPerformanceOverlayChanged,
this.checkerboardRasterCacheImages,
this.onCheckerboardRasterCacheImagesChanged,
this.checkerboardOffscreenLayers,
this.onCheckerboardOffscreenLayersChanged,
this.onPlatformChanged,
this.overrideDirection: TextDirection.ltr,
this.onOverrideDirectionChanged,
this.onSendFeedback,
}) : assert(onThemeChanged != null),
assert(onTimeDilationChanged != null),
super(key: key);
final GalleryTheme galleryTheme;
final ValueChanged<GalleryTheme> onThemeChanged;
final double timeDilation;
final ValueChanged<double> onTimeDilationChanged;
final double textScaleFactor;
final ValueChanged<double> onTextScaleFactorChanged;
final bool showPerformanceOverlay;
final ValueChanged<bool> onShowPerformanceOverlayChanged;
final bool checkerboardRasterCacheImages;
final ValueChanged<bool> onCheckerboardRasterCacheImagesChanged;
final bool checkerboardOffscreenLayers;
final ValueChanged<bool> onCheckerboardOffscreenLayersChanged;
final ValueChanged<TargetPlatform> onPlatformChanged;
final TextDirection overrideDirection;
final ValueChanged<TextDirection> onOverrideDirectionChanged;
final VoidCallback onSendFeedback;
@override
GalleryHomeState createState() => new GalleryHomeState();
}
class GalleryHomeState extends State<GalleryHome> with SingleTickerProviderStateMixin {
static final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = new AnimationController(
duration: const Duration(milliseconds: 600),
debugLabel: 'preview banner',
vsync: this,
)..forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
List<Widget> _galleryListItems() {
final List<Widget> listItems = <Widget>[];
final ThemeData themeData = Theme.of(context);
final TextStyle headerStyle = themeData.textTheme.body2.copyWith(color: themeData.accentColor);
String category;
for (GalleryItem galleryItem in kAllGalleryItems) {
if (category != galleryItem.category) {
if (category != null)
listItems.add(const Divider());
listItems.add(
new MergeSemantics(
child: new Container(
height: 48.0,
padding: const EdgeInsetsDirectional.only(start: 16.0),
alignment: AlignmentDirectional.centerStart,
child: new SafeArea(
top: false,
bottom: false,
child: new Semantics(
header: true,
child: new Text(galleryItem.category, style: headerStyle),
),
),
),
)
);
category = galleryItem.category;
}
listItems.add(galleryItem);
}
return listItems;
}
@override
Widget build(BuildContext context) {
Widget home = new Scaffold(
key: _scaffoldKey,
drawer: new GalleryDrawer(
galleryTheme: widget.galleryTheme,
onThemeChanged: widget.onThemeChanged,
timeDilation: widget.timeDilation,
onTimeDilationChanged: widget.onTimeDilationChanged,
textScaleFactor: widget.textScaleFactor,
onTextScaleFactorChanged: widget.onTextScaleFactorChanged,
showPerformanceOverlay: widget.showPerformanceOverlay,
onShowPerformanceOverlayChanged: widget.onShowPerformanceOverlayChanged,
checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,
onCheckerboardRasterCacheImagesChanged: widget.onCheckerboardRasterCacheImagesChanged,
checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
onCheckerboardOffscreenLayersChanged: widget.onCheckerboardOffscreenLayersChanged,
onPlatformChanged: widget.onPlatformChanged,
overrideDirection: widget.overrideDirection,
onOverrideDirectionChanged: widget.onOverrideDirectionChanged,
onSendFeedback: widget.onSendFeedback,
),
body: new CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: _kFlexibleSpaceMaxHeight,
flexibleSpace: const FlexibleSpaceBar(
title: const Text('Flutter Gallery'),
// TODO(abarth): Wire up to the parallax in a way that doesn't pop during hero transition.
background: const _AppBarBackground(animation: kAlwaysDismissedAnimation),
),
),
new SliverList(delegate: new SliverChildListDelegate(_galleryListItems())),
],
)
);
// In checked mode our MaterialApp will show the default "debug" banner.
// Otherwise show the "preview" banner.
bool showPreviewBanner = true;
assert(() {
showPreviewBanner = false;
return true;
}());
if (showPreviewBanner) {
home = new Stack(
fit: StackFit.expand,
children: <Widget>[
home,
new FadeTransition(
opacity: new CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
child: const Banner(
message: 'PREVIEW',
location: BannerLocation.topEnd,
)
),
]
);
}
return home;
}
}