diff --git a/packages/flutter/lib/foundation.dart b/packages/flutter/lib/foundation.dart index a463f554bc..21cb3f2ab8 100644 --- a/packages/flutter/lib/foundation.dart +++ b/packages/flutter/lib/foundation.dart @@ -14,5 +14,6 @@ export 'src/foundation/basic_types.dart'; export 'src/foundation/binding.dart'; export 'src/foundation/change_notifier.dart'; export 'src/foundation/licenses.dart'; +export 'src/foundation/platform.dart'; export 'src/foundation/print.dart'; export 'src/foundation/synchronous_future.dart'; diff --git a/packages/flutter/lib/src/foundation/platform.dart b/packages/flutter/lib/src/foundation/platform.dart new file mode 100644 index 0000000000..01bd4892e0 --- /dev/null +++ b/packages/flutter/lib/src/foundation/platform.dart @@ -0,0 +1,12 @@ +// 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. + +/// The platform that user interaction should adapt to target. +enum TargetPlatform { + /// Android: + android, + + /// iOS: + iOS, +} diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index 83b215a100..d79d3d2e4b 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io' show Platform; - import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -25,6 +23,15 @@ const TextStyle _errorTextStyle = const TextStyle( decorationStyle: TextDecorationStyle.double ); +/// The visual and interaction design for overscroll. +enum OverscrollStyle { + /// Overscrolls are clamped and indicated with a glow. + glow, + + /// Overscrolls are not clamped and indicated with elastic physics. + bounce +} + /// An application that uses material design. /// /// A convenience widget that wraps a number of widgets that are commonly @@ -53,6 +60,7 @@ class MaterialApp extends StatefulWidget { this.theme, this.home, this.routes: const {}, + this.overscrollStyle, this.onGenerateRoute, this.onLocaleChanged, this.debugShowMaterialGrid: false, @@ -104,6 +112,11 @@ class MaterialApp extends StatefulWidget { /// build the page instead. final Map routes; + /// The visual and interaction design for overscroll. + /// + /// Defaults to being adapted to the current [TargetPlatform]. + final OverscrollStyle overscrollStyle; + /// The route generator callback used when the app is navigated to a /// named route. final RouteFactory onGenerateRoute; @@ -149,7 +162,8 @@ class _IndicatorScrollConfigurationDelegate extends ScrollConfigurationDelegate @override Widget wrapScrollWidget(Widget scrollWidget) => new OverscrollIndicator(child: scrollWidget); } -final ScrollConfigurationDelegate _indicatorScroll = new _IndicatorScrollConfigurationDelegate(); + +final ScrollConfigurationDelegate _glowScroll = new _IndicatorScrollConfigurationDelegate(); final ScrollConfigurationDelegate _bounceScroll = new ScrollConfigurationDelegate(); class _MaterialAppState extends State { @@ -180,6 +194,24 @@ class _MaterialAppState extends State { return null; } + ScrollConfigurationDelegate _getScrollDelegate(TargetPlatform platform) { + if (config.overscrollStyle != null) { + switch (config.overscrollStyle) { + case OverscrollStyle.glow: + return _glowScroll; + case OverscrollStyle.bounce: + return _bounceScroll; + } + } + switch (platform) { + case TargetPlatform.android: + return _glowScroll; + case TargetPlatform.iOS: + return _bounceScroll; + } + return null; + } + @override Widget build(BuildContext context) { ThemeData theme = config.theme ?? new ThemeData.fallback(); @@ -213,7 +245,7 @@ class _MaterialAppState extends State { }); return new ScrollConfiguration( - delegate: (Platform.isIOS || Platform.isMacOS) ? _bounceScroll : _indicatorScroll, + delegate: _getScrollDelegate(theme.platform), child: result ); } diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index ca6faf16ec..78241fba95 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; import 'dart:ui' show Color, hashValues; +import 'package:flutter/foundation.dart'; + import 'colors.dart'; import 'icon_theme_data.dart'; import 'typography.dart'; @@ -90,7 +93,8 @@ class ThemeData { TextTheme textTheme, TextTheme primaryTextTheme, IconThemeData iconTheme, - IconThemeData primaryIconTheme + IconThemeData primaryIconTheme, + TargetPlatform platform }) { brightness ??= Brightness.light; final bool isDark = brightness == Brightness.dark; @@ -121,6 +125,7 @@ class ThemeData { primaryTextTheme ??= primaryIsDark ? Typography.white : Typography.black; iconTheme ??= isDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black); primaryIconTheme ??= primaryIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black); + platform ??= (Platform.isIOS || Platform.isMacOS) ? TargetPlatform.iOS : TargetPlatform.android; return new ThemeData.raw( brightness: brightness, primaryColor: primaryColor, @@ -146,7 +151,8 @@ class ThemeData { textTheme: textTheme, primaryTextTheme: primaryTextTheme, iconTheme: iconTheme, - primaryIconTheme: primaryIconTheme + primaryIconTheme: primaryIconTheme, + platform: platform ); } @@ -181,7 +187,8 @@ class ThemeData { this.textTheme, this.primaryTextTheme, this.iconTheme, - this.primaryIconTheme + this.primaryIconTheme, + this.platform }) { assert(brightness != null); assert(primaryColor != null); @@ -208,6 +215,7 @@ class ThemeData { assert(primaryTextTheme != null); assert(iconTheme != null); assert(primaryIconTheme != null); + assert(platform != null); } /// A default light blue theme. @@ -320,6 +328,11 @@ class ThemeData { /// An icon theme that contrasts with the primary color. final IconThemeData primaryIconTheme; + /// The platform the material widgets should adapt to target. + /// + /// Defaults to the current platform. + final TargetPlatform platform; + /// Linearly interpolate between two themes. static ThemeData lerp(ThemeData begin, ThemeData end, double t) { return new ThemeData.raw( @@ -347,7 +360,8 @@ class ThemeData { textTheme: TextTheme.lerp(begin.textTheme, end.textTheme, t), primaryTextTheme: TextTheme.lerp(begin.primaryTextTheme, end.primaryTextTheme, t), iconTheme: IconThemeData.lerp(begin.iconTheme, end.iconTheme, t), - primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t) + primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t), + platform: t < 0.5 ? begin.platform : end.platform ); }