Revert "[lib.widgets] Remove unused widgets."
This reverts commit 311b0e3a787e57f40313a9fc53a56c1994934ab2.
Reason for revert: Breaks build during global integration
Original change's description:
> [lib.widgets] Remove unused widgets.
>
> Test: built and ran
>
> Change-Id: Ic53608ad0429f076540a75341542c9ebc255bf2f
TBR=jamesr@google.com,anwilson@google.com,ejia@google.com
Change-Id: I1ed7adb90c47bdb5815573236c16eb10a4426b7c
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
diff --git a/bin/bluetooth_settings/lib/main.dart b/bin/bluetooth_settings/lib/main.dart
index da339e8..f72a9ac 100644
--- a/bin/bluetooth_settings/lib/main.dart
+++ b/bin/bluetooth_settings/lib/main.dart
@@ -4,7 +4,7 @@
import 'package:flutter/material.dart';
import 'package:lib.app.dart/logging.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'src/bluetooth_model.dart';
import 'src/bluetooth_settings.dart';
diff --git a/bin/display_settings/lib/main.dart b/bin/display_settings/lib/main.dart
index d6fbf94..a6db72a 100644
--- a/bin/display_settings/lib/main.dart
+++ b/bin/display_settings/lib/main.dart
@@ -3,10 +3,9 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
-import 'package:lib.app.dart/app.dart' show StartupContext;
import 'package:lib.app.dart/logging.dart';
import 'package:lib.display.flutter/display_policy_brightness_model.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'src/widget.dart';
/// Main entry point to the display settings module.
diff --git a/bin/userpicker_base_shell/lib/main.dart b/bin/userpicker_base_shell/lib/main.dart
index c57e8e5..2fadd48 100644
--- a/bin/userpicker_base_shell/lib/main.dart
+++ b/bin/userpicker_base_shell/lib/main.dart
@@ -8,10 +8,10 @@
import 'package:flutter/material.dart';
import 'package:lib.app.dart/app.dart';
import 'package:lib.app.dart/logging.dart';
-import 'package:lib.base_shell/base_shell_widget.dart';
import 'package:lib.base_shell/netstack_model.dart';
import 'package:lib.widgets/application_deprecated.dart';
import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'package:meta/meta.dart';
import 'package:zircon/zircon.dart';
diff --git a/bin/wifi_settings/lib/main.dart b/bin/wifi_settings/lib/main.dart
index 61fb059..51e6c8c 100644
--- a/bin/wifi_settings/lib/main.dart
+++ b/bin/wifi_settings/lib/main.dart
@@ -4,7 +4,7 @@
import 'package:flutter/material.dart';
import 'package:lib.app.dart/logging.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'src/fuchsia/wifi_settings_model.dart';
import 'src/wlan_manager.dart';
diff --git a/bin/xi/xi_session_demo/lib/main.dart b/bin/xi/xi_session_demo/lib/main.dart
index 4897794..cee62e0 100644
--- a/bin/xi/xi_session_demo/lib/main.dart
+++ b/bin/xi/xi_session_demo/lib/main.dart
@@ -4,7 +4,7 @@
import 'package:flutter/material.dart';
import 'package:lib.app.dart/logging.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'src/demo_model.dart';
import 'src/demo_widget.dart';
diff --git a/bin/xi/xi_session_demo/lib/src/demo_widget.dart b/bin/xi/xi_session_demo/lib/src/demo_widget.dart
index e9b78f2..1254193 100644
--- a/bin/xi/xi_session_demo/lib/src/demo_widget.dart
+++ b/bin/xi/xi_session_demo/lib/src/demo_widget.dart
@@ -4,7 +4,7 @@
import 'package:flutter/material.dart';
import 'package:lib.ui.flutter/child_view.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'demo_model.dart';
diff --git a/examples/tictactoe/bin/tictactoe/game_board_mod/lib/main.dart b/examples/tictactoe/bin/tictactoe/game_board_mod/lib/main.dart
index c597242..a9ad760 100644
--- a/examples/tictactoe/bin/tictactoe/game_board_mod/lib/main.dart
+++ b/examples/tictactoe/bin/tictactoe/game_board_mod/lib/main.dart
@@ -10,7 +10,7 @@
import 'package:lib.app.dart/logging.dart';
import 'package:lib.app_driver.dart/module_driver.dart';
import 'package:lib.proposal.dart/proposal.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'package:tictactoe_common/common.dart';
import 'src/model/tictactoe_model.dart';
diff --git a/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/square.dart b/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/square.dart
index 4524d37..1b04deb 100644
--- a/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/square.dart
+++ b/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/square.dart
@@ -3,7 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'package:tictactoe_common/common.dart' show SquareState;
import '../model/tictactoe_model.dart';
diff --git a/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/tictactoe_board.dart b/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/tictactoe_board.dart
index a12d692..0a48f65 100644
--- a/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/tictactoe_board.dart
+++ b/examples/tictactoe/bin/tictactoe/game_board_mod/lib/src/widget/tictactoe_board.dart
@@ -3,7 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'package:tictactoe_common/common.dart' show GameState;
import '../model/tictactoe_model.dart';
diff --git a/examples/tictactoe/bin/tictactoe/game_board_mod/test/mod_test.dart b/examples/tictactoe/bin/tictactoe/game_board_mod/test/mod_test.dart
index dda8bd8..e85296a 100644
--- a/examples/tictactoe/bin/tictactoe/game_board_mod/test/mod_test.dart
+++ b/examples/tictactoe/bin/tictactoe/game_board_mod/test/mod_test.dart
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'package:topaz.examples.tictactoe.bin.tictactoe.game_board_mod._tictactoe_game_board_mod_dart_library/src/model/tictactoe_model.dart'; // ignore: implementation_imports
import 'package:topaz.examples.tictactoe.bin.tictactoe.game_board_mod._tictactoe_game_board_mod_dart_library/src/widget/square.dart'; // ignore: implementation_imports
import 'package:topaz.examples.tictactoe.bin.tictactoe.game_board_mod._tictactoe_game_board_mod_dart_library/src/widget/tictactoe_board.dart'; // ignore: implementation_imports
diff --git a/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/main.dart b/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/main.dart
index e09a398..7bc0650 100644
--- a/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/main.dart
+++ b/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/main.dart
@@ -6,7 +6,7 @@
import 'package:flutter/material.dart';
import 'package:lib.app.dart/logging.dart';
import 'package:lib.app_driver.dart/module_driver.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import 'package:tictactoe_common/common.dart';
import 'src/model/scoreboard_model.dart';
diff --git a/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/src/widget/scoreboard_widget.dart b/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/src/widget/scoreboard_widget.dart
index 90eb417..8913a87 100644
--- a/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/src/widget/scoreboard_widget.dart
+++ b/examples/tictactoe/bin/tictactoe/scoreboard_mod/lib/src/widget/scoreboard_widget.dart
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
-import 'package:lib.widgets/model.dart';
+import 'package:lib.widgets/modular.dart';
import '../model/scoreboard_model.dart';
diff --git a/lib/base_shell/lib/base_model.dart b/lib/base_shell/lib/base_model.dart
index 958e179..a9a6ef0 100644
--- a/lib/base_shell/lib/base_model.dart
+++ b/lib/base_shell/lib/base_model.dart
@@ -17,10 +17,10 @@
import 'package:lib.app.dart/app.dart' as app;
import 'package:lib.app.dart/logging.dart';
import 'package:lib.ui.flutter/child_view.dart';
+import 'package:lib.widgets/modular.dart';
import 'package:meta/meta.dart';
import 'package:zircon/zircon.dart' show Channel;
-import 'base_shell_model.dart';
import 'netstack_model.dart';
import 'user_manager.dart';
@@ -273,8 +273,7 @@
super.onReady(userProvider, baseShellContext, presentation);
final netstackProxy = NetstackProxy();
- app.connectToService(
- app.StartupContext.fromStartupInfo().environmentServices,
+ app.connectToService(StartupContext.fromStartupInfo().environmentServices,
netstackProxy.ctrl);
_netstackModel = NetstackModel(netstack: netstackProxy)..start();
diff --git a/public/dart/widgets/BUILD.gn b/public/dart/widgets/BUILD.gn
index 10a37d9..7de0643 100644
--- a/public/dart/widgets/BUILD.gn
+++ b/public/dart/widgets/BUILD.gn
@@ -20,12 +20,17 @@
"src/model/spring_model.dart",
"src/model/ticking_model.dart",
"src/model/tracing_spring_model.dart",
+ "src/modular/base_shell_model.dart",
+ "src/modular/base_shell_widget.dart",
"src/modular/dank_session_shell_widget.dart",
"src/widgets/alphatar.dart",
"src/widgets/conditional_builder.dart",
"src/widgets/fuchsia_spinner.dart",
"src/widgets/future_widget.dart",
+ "src/widgets/mondrian_spinner.dart",
"src/widgets/rk4_spring_simulation.dart",
+ "src/widgets/shadowed_text.dart",
+ "src/widgets/text_placeholder.dart",
"src/widgets/ticking_state.dart",
"src/widgets/window_media_query.dart",
"widgets.dart",
diff --git a/public/dart/widgets/lib/modular.dart b/public/dart/widgets/lib/modular.dart
index 056442e..e26b2de 100644
--- a/public/dart/widgets/lib/modular.dart
+++ b/public/dart/widgets/lib/modular.dart
@@ -5,4 +5,6 @@
// WARNING(MS-1613): All Fuchsia system dependencies will be separated out from
// this Flutter specific Dart package. If you need Fuchsia system interactions
// use //topaz/public/lib/app_driver/dart or the Dart FIDL Bindings directly.
+export 'src/modular/base_shell_model.dart';
+export 'src/modular/base_shell_widget.dart';
export 'src/modular/dank_session_shell_widget.dart';
diff --git a/public/dart/widgets/lib/src/modular/base_shell_model.dart b/public/dart/widgets/lib/src/modular/base_shell_model.dart
new file mode 100644
index 0000000..628bec1
--- /dev/null
+++ b/public/dart/widgets/lib/src/modular/base_shell_model.dart
@@ -0,0 +1,43 @@
+// Copyright 2017 The Fuchsia 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:fidl_fuchsia_modular/fidl.dart';
+import 'package:fidl_fuchsia_ui_policy/fidl.dart';
+import 'package:lib.widgets/model.dart';
+import 'package:meta/meta.dart';
+
+export 'package:lib.widgets/model.dart' show ScopedModel, ScopedModelDescendant;
+
+/// The [Model] that provides a [BaseShellContext] and [UserProvider].
+class BaseShellModel extends Model {
+ BaseShellContext _baseShellContext;
+ UserProvider _userProvider;
+ Presentation _presentation;
+
+ /// The [BaseShellContext] given to this app's [BaseShell].
+ BaseShellContext get baseShellContext => _baseShellContext;
+
+ /// The [UserProvider] given to this app's [BaseShell].
+ UserProvider get userProvider => _userProvider;
+
+ /// The [Presentation] given to this app's [BaseShell].
+ Presentation get presentation => _presentation;
+
+ /// Called when this app's [BaseShell] is given its [BaseShellContext],
+ /// and [UserProvider], and (optionally) its [Presentation].
+ @mustCallSuper
+ void onReady(
+ UserProvider userProvider,
+ BaseShellContext baseShellContext,
+ Presentation presentation,
+ ) {
+ _userProvider = userProvider;
+ _baseShellContext = baseShellContext;
+ _presentation = presentation;
+ notifyListeners();
+ }
+
+ /// Called when the app's [BaseShell] stops.
+ void onStop() => null;
+}
diff --git a/public/dart/widgets/lib/src/modular/base_shell_widget.dart b/public/dart/widgets/lib/src/modular/base_shell_widget.dart
new file mode 100644
index 0000000..1b90793
--- /dev/null
+++ b/public/dart/widgets/lib/src/modular/base_shell_widget.dart
@@ -0,0 +1,103 @@
+// Copyright 2017 The Fuchsia 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:lib.app.dart/app.dart';
+import 'package:fidl_fuchsia_auth/fidl.dart';
+import 'package:fidl_fuchsia_modular/fidl.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:fidl/fidl.dart';
+import 'package:lib.device.dart/device.dart';
+import 'package:meta/meta.dart';
+
+import '../widgets/window_media_query.dart';
+import 'base_shell_model.dart';
+
+/// A wrapper widget intended to be the root of the application that is
+/// a [BaseShell]. Its main purpose is to hold the [StartupContext] and
+/// [BaseShell] instances so they aren't garbage collected.
+/// For convenience, [advertise] does the advertising of the app as a
+/// [BaseShell] to the rest of the system via the [StartupContext].
+/// Also for convienence, the [BaseShellModel] given to this widget
+/// will be made available to [child] and [child]'s descendants.
+class BaseShellWidget<T extends BaseShellModel> extends StatelessWidget {
+ /// The [StartupContext] to [advertise] its [BaseShell] services to.
+ final StartupContext startupContext;
+
+ /// The bindings for the [BaseShell] service implemented by [BaseShellImpl].
+ final Set<BaseShellBinding> _baseShellBindingSet =
+ new Set<BaseShellBinding>();
+
+ /// The bindings for the [Lifecycle] service implemented by [BaseShellImpl].
+ final Set<LifecycleBinding> _lifecycleBindingSet =
+ new Set<LifecycleBinding>();
+
+ /// The [BaseShell] to [advertise].
+ final BaseShellImpl _baseShell;
+
+ /// The rest of the application.
+ final Widget child;
+
+ final T _baseShellModel;
+
+ /// Constructor.
+ BaseShellWidget({
+ @required this.startupContext,
+ T baseShellModel,
+ AuthenticationUiContext authenticationUiContext,
+ this.child,
+ }) : _baseShellModel = baseShellModel,
+ _baseShell = _createBaseShell(
+ baseShellModel,
+ authenticationUiContext,
+ );
+
+ @override
+ Widget build(BuildContext context) => new MaterialApp(
+ home: new Material(
+ child: new Directionality(
+ textDirection: TextDirection.ltr,
+ child: new WindowMediaQuery(
+ child: _baseShellModel == null
+ ? child
+ : new ScopedModel<T>(model: _baseShellModel, child: child),
+ ),
+ ),
+ ),
+ );
+
+ /// Advertises [_baseShell] as a [BaseShell] to the rest of the system via
+ /// the [StartupContext].
+ void advertise() {
+ startupContext.outgoingServices
+ ..addServiceForName((InterfaceRequest<BaseShell> request) {
+ BaseShellBinding binding = new BaseShellBinding()
+ ..bind(_baseShell, request);
+ _baseShellBindingSet.add(binding);
+ }, BaseShell.$serviceName)
+ ..addServiceForName((InterfaceRequest<Lifecycle> request) {
+ LifecycleBinding binding = new LifecycleBinding()
+ ..bind(_baseShell, request);
+ _lifecycleBindingSet.add(binding);
+ }, Lifecycle.$serviceName);
+ }
+
+ static BaseShell _createBaseShell(
+ BaseShellModel baseShellModel,
+ AuthenticationUiContext authenticationUiContext,
+ ) {
+ return new BaseShellImpl(
+ authenticationUiContext: authenticationUiContext,
+ onReady: baseShellModel?.onReady,
+ onStop: () {
+ baseShellModel?.onStop?.call();
+ },
+ );
+ }
+
+ /// Cancels any authentication flow currently in progress.
+ void cancelAuthenticationFlow() {
+ _baseShell.closeAuthenticationUiContextBindings();
+ }
+}
diff --git a/public/dart/widgets/lib/src/widgets/mondrian_spinner.dart b/public/dart/widgets/lib/src/widgets/mondrian_spinner.dart
new file mode 100644
index 0000000..17dd489
--- /dev/null
+++ b/public/dart/widgets/lib/src/widgets/mondrian_spinner.dart
@@ -0,0 +1,177 @@
+// Copyright 2017 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 'dart:math' as math;
+
+import 'package:flutter/material.dart';
+
+const double _kBorderWidth = 4.0;
+const double _kBorderRadius = 8.0;
+const double _kClipRadius = 12.0;
+
+const int _kBlueStartingIndex = 0;
+const int _kYellowStartingIndex = 3;
+
+/// The mondrian spinner. A series of overlapping rectangular elements that
+/// rotate. The parent of this [MondrianSpinner] must have non-infinite bounds.
+/// The [MondrianSpinner] will center itself in its parent and have a diameter
+/// equal to the minimum dimension.
+class MondrianSpinner extends StatefulWidget {
+ @override
+ _MondrianSpinnerState createState() => new _MondrianSpinnerState();
+}
+
+class _MondrianSpinnerState extends State<MondrianSpinner> {
+ Timer _timer;
+ int _index = 0;
+ List<Rect> _positions;
+
+ @override
+ void initState() {
+ super.initState();
+ _timer = new Timer.periodic(
+ const Duration(milliseconds: 500),
+ (_) => setState(() {
+ _index = (_index + 1) % _positions.length;
+ }),
+ );
+ }
+
+ @override
+ void dispose() {
+ _timer?.cancel();
+ _timer = null;
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) => new LayoutBuilder(
+ builder: (BuildContext context, BoxConstraints constraints) {
+ double diameter = math.min(
+ constraints.maxWidth,
+ constraints.maxHeight,
+ );
+ _positions = <Rect>[
+ new Rect.fromLTWH(
+ 0.0,
+ 0.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ diameter,
+ ),
+ new Rect.fromLTWH(
+ 0.0,
+ diameter / 2.0 - _kBorderWidth / 2.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ ),
+ new Rect.fromLTWH(
+ 0.0,
+ diameter / 2.0 - _kBorderWidth / 2.0,
+ diameter,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ ),
+ new Rect.fromLTWH(
+ diameter / 2.0 - _kBorderWidth / 2.0,
+ diameter / 2.0 - _kBorderWidth / 2.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ ),
+ new Rect.fromLTWH(
+ diameter / 2.0 - _kBorderWidth / 2.0,
+ 0.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ diameter,
+ ),
+ new Rect.fromLTWH(
+ diameter / 2.0 - _kBorderWidth / 2.0,
+ 0.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ ),
+ new Rect.fromLTWH(
+ 0.0,
+ 0.0,
+ diameter,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ ),
+ new Rect.fromLTWH(
+ 0.0,
+ 0.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ diameter / 2.0 + _kBorderWidth / 2.0,
+ ),
+ ];
+
+ return new Center(
+ child: new Container(
+ width: diameter,
+ height: diameter,
+ decoration: new BoxDecoration(
+ color: Colors.black,
+ borderRadius: new BorderRadius.circular(_kBorderRadius),
+ ),
+ foregroundDecoration: new BoxDecoration(
+ border: new Border.all(
+ color: Colors.black,
+ width: _kBorderWidth,
+ ),
+ borderRadius: new BorderRadius.circular(_kBorderRadius),
+ ),
+ child: new ClipRRect(
+ borderRadius: new BorderRadius.circular(_kClipRadius),
+ child: new Stack(
+ fit: StackFit.passthrough,
+ children: <Widget>[
+ new Container(
+ margin: const EdgeInsets.all(_kBorderWidth),
+ color: Colors.red[700],
+ ),
+ new AnimatedPositioned(
+ left: _yellowPosition.left,
+ top: _yellowPosition.top,
+ width: _yellowPosition.width,
+ height: _yellowPosition.height,
+ curve: Curves.fastOutSlowIn,
+ duration: const Duration(milliseconds: 250),
+ child: new Container(
+ color: Colors.yellow[700],
+ foregroundDecoration: new BoxDecoration(
+ border: new Border.all(
+ color: Colors.black,
+ width: _kBorderWidth,
+ ),
+ ),
+ ),
+ ),
+ new AnimatedPositioned(
+ left: _bluePosition.left,
+ top: _bluePosition.top,
+ width: _bluePosition.width,
+ height: _bluePosition.height,
+ curve: Curves.fastOutSlowIn,
+ duration: const Duration(milliseconds: 250),
+ child: new Container(
+ color: Colors.blue[700],
+ foregroundDecoration: new BoxDecoration(
+ border: new Border.all(
+ color: Colors.black,
+ width: _kBorderWidth,
+ ),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ );
+
+ Rect get _bluePosition =>
+ _positions[(_index + _kBlueStartingIndex) % _positions.length];
+ Rect get _yellowPosition =>
+ _positions[(_index + _kYellowStartingIndex) % _positions.length];
+}
diff --git a/public/dart/widgets/lib/src/widgets/shadowed_text.dart b/public/dart/widgets/lib/src/widgets/shadowed_text.dart
new file mode 100644
index 0000000..fc552f1
--- /dev/null
+++ b/public/dart/widgets/lib/src/widgets/shadowed_text.dart
@@ -0,0 +1,51 @@
+// Copyright 2018 The Fuchsia 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:meta/meta.dart';
+
+/// Function that returns a stylized widget with the specified text and
+/// color.
+typedef TextBuilder = Widget Function(
+ BuildContext context, String text, Color color);
+
+/// Text overlaid over another copy of text with shadow color.
+class ShadowedText extends StatelessWidget {
+ /// Function that returns the text widget
+ final TextBuilder textBuilder;
+
+ /// Text to display
+ final String text;
+
+ /// Shadow color
+ final Color shadowColor;
+
+ /// Text color
+ final Color textColor;
+
+ /// Constructor
+ const ShadowedText({
+ @required this.textBuilder,
+ @required this.text,
+ @required this.shadowColor,
+ @required this.textColor,
+ }) : assert(textBuilder != null),
+ assert(text != null),
+ assert(shadowColor != null),
+ assert(textColor != null);
+
+ @override
+ Widget build(BuildContext context) {
+ return new Stack(
+ fit: StackFit.passthrough,
+ children: <Widget>[
+ new Transform(
+ transform: new Matrix4.translationValues(2.0, 2.0, 0.0),
+ child: textBuilder(context, text, shadowColor),
+ ),
+ textBuilder(context, text, textColor),
+ ],
+ );
+ }
+}
diff --git a/public/dart/widgets/lib/src/widgets/text_placeholder.dart b/public/dart/widgets/lib/src/widgets/text_placeholder.dart
new file mode 100644
index 0000000..9a4db77
--- /dev/null
+++ b/public/dart/widgets/lib/src/widgets/text_placeholder.dart
@@ -0,0 +1,35 @@
+// Copyright 2017 The Fuchsia 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:meta/meta.dart';
+
+final Color _kPlaceholderColor = Colors.grey[300];
+
+/// Renders a text placeholder meant for loading/scaffolding
+class TextPlaceholder extends StatelessWidget {
+ /// Constructor
+ const TextPlaceholder({
+ @required this.style,
+ this.width = 80.0,
+ }) : assert(style != null);
+
+ /// Style of the text that this placeholder is used for
+ final TextStyle style;
+
+ /// Width that this placeholder should take up
+ final double width;
+
+ @override
+ Widget build(BuildContext context) {
+ return new Container(
+ margin: new EdgeInsets.symmetric(
+ vertical: (style.fontSize * style.height - style.fontSize) / 2.0,
+ ),
+ color: _kPlaceholderColor,
+ height: style.fontSize,
+ width: width,
+ );
+ }
+}
diff --git a/public/dart/widgets/lib/widgets.dart b/public/dart/widgets/lib/widgets.dart
index 2897c7b..ac66aeb 100644
--- a/public/dart/widgets/lib/widgets.dart
+++ b/public/dart/widgets/lib/widgets.dart
@@ -12,7 +12,10 @@
export 'src/widgets/fuchsia_spinner.dart';
export 'src/widgets/future_widget.dart';
export 'src/widgets/lottie_player.dart';
+export 'src/widgets/mondrian_spinner.dart';
export 'src/widgets/rk4_spring_simulation.dart';
export 'src/widgets/screen_container.dart';
+export 'src/widgets/shadowed_text.dart';
+export 'src/widgets/text_placeholder.dart';
export 'src/widgets/ticking_state.dart';
export 'src/widgets/window_media_query.dart';