[terminal_settings] Add initial settings container.

Adds a simple widget for managing multiple settings panes.

Change-Id: Ib39e09cad35fb716163c0fb04ad195e5a2a1de6c
diff --git a/bin/BUILD.gn b/bin/BUILD.gn
index b7819db..7bd522a 100644
--- a/bin/BUILD.gn
+++ b/bin/BUILD.gn
@@ -5,6 +5,7 @@
 group("bin") {
   deps = [
     "simple_browser",
+    "terminal_settings",
   ]
 }
 
@@ -21,5 +22,6 @@
 
   deps = [
     "simple_browser:simple_browser_unittests($host_toolchain)",
+    "terminal_settings:terminal_settings_unittests($host_toolchain)",
   ]
 }
diff --git a/bin/terminal_settings/BUILD.gn b/bin/terminal_settings/BUILD.gn
new file mode 100644
index 0000000..2ef473d
--- /dev/null
+++ b/bin/terminal_settings/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2019 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("//topaz/runtime/dart/flutter_test.gni")
+import("//topaz/runtime/flutter_runner/flutter_app.gni")
+
+flutter_app("terminal_settings") {
+  main_dart = "lib/main.dart"
+
+  sources = [
+    "src/widgets/settings_container.dart",
+    "src/widgets/settings_pane.dart",
+  ]
+
+  meta = [
+    {
+      path = rebase_path("meta/terminal_settings.cmx")
+      dest = "terminal_settings.cmx"
+    },
+  ]
+
+  package_name = "terminal_settings"
+
+  manifest = "pubspec.yaml"
+
+  deps = [
+    "//third_party/dart-pkg/git/flutter/packages/flutter",
+    "//topaz/public/dart/fuchsia_logger",
+  ]
+}
+
+flutter_test("terminal_settings_unittests") {
+  sources = [
+    "settings_container_test.dart",
+  ]
+
+  deps = [
+    ":terminal_settings_dart_library",
+    "//third_party/dart-pkg/git/flutter/packages/flutter_test",
+    "//third_party/dart-pkg/pub/test",
+  ]
+}
diff --git a/bin/terminal_settings/README.md b/bin/terminal_settings/README.md
new file mode 100644
index 0000000..0ce7f77
--- /dev/null
+++ b/bin/terminal_settings/README.md
@@ -0,0 +1,11 @@
+# Terminal Settings
+
+Module which allows users to update settings which can be read 
+from the terminal module.
+
+## Testing
+
+To run unit tests
+
+    fx set workstation.<BOARD> --with //src/experiences:tests
+    fx run-host-tests terminal_settings_unittests
diff --git a/bin/terminal_settings/analysis_options.yaml b/bin/terminal_settings/analysis_options.yaml
new file mode 100644
index 0000000..bebf512
--- /dev/null
+++ b/bin/terminal_settings/analysis_options.yaml
@@ -0,0 +1,5 @@
+# Copyright 2019 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.
+
+include: ../../analysis_options.yaml
diff --git a/bin/terminal_settings/lib/main.dart b/bin/terminal_settings/lib/main.dart
new file mode 100644
index 0000000..6267779
--- /dev/null
+++ b/bin/terminal_settings/lib/main.dart
@@ -0,0 +1,39 @@
+// Copyright 2019 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:fuchsia_logger/logger.dart';
+
+import 'src/widgets/settings_container.dart';
+import 'src/widgets/settings_pane.dart';
+
+void main() {
+  setupLogger(name: 'terminal_settings');
+  runApp(
+    MaterialApp(
+      theme: _themeData,
+      home: Scaffold(
+        body: SettingsContainer([
+          ProfilesSetttingsPane(),
+        ]),
+      ),
+    ),
+  );
+}
+
+final _themeData = ThemeData(
+  backgroundColor: Color(0xE6E6E6FF),
+  colorScheme: ColorScheme.light(
+    background: Color(0xE6E6E6FF),
+    primary: Colors.white,
+    secondary: Colors.black,
+  ),
+  fontFamily: 'RobotoMono',
+  dividerTheme: DividerThemeData(
+    color: Colors.black,
+    space: 4.0,
+    endIndent: 16.0,
+    indent: 16.0,
+  ),
+);
diff --git a/bin/terminal_settings/lib/src/widgets/settings_container.dart b/bin/terminal_settings/lib/src/widgets/settings_container.dart
new file mode 100644
index 0000000..71abaee
--- /dev/null
+++ b/bin/terminal_settings/lib/src/widgets/settings_container.dart
@@ -0,0 +1,69 @@
+// Copyright 2019 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 'settings_pane.dart';
+
+/// A widget for handling multiple settings panes.
+///
+/// Draws a button bar at the top which allows a user
+/// to select different panes to display and swaps out their
+/// content in the remaining space below.
+class SettingsContainer extends StatelessWidget {
+  final List<SettingsPane> panes;
+  final ValueNotifier<SettingsPane> currentPane =
+      ValueNotifier<SettingsPane>(null);
+
+  SettingsContainer(this.panes) {
+    if (panes.isNotEmpty) {
+      currentPane.value = panes.first;
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) => Container(
+        color: Theme.of(context).colorScheme.background,
+        child: AnimatedBuilder(
+          animation: currentPane,
+          builder: (context, _) => Column(
+            children: [
+              _topBar(context),
+              _bottomContent(context),
+            ],
+          ),
+        ),
+      );
+
+  Widget _topBar(BuildContext context) {
+    final colorScheme = Theme.of(context).colorScheme;
+    return Column(
+      children: [
+        ButtonBar(
+          alignment: MainAxisAlignment.start,
+          children: panes.map((pane) {
+            final selected = currentPane.value == pane;
+            return FlatButton(
+              child: Text(
+                pane.title,
+              ),
+              onPressed: () {
+                currentPane.value = pane;
+              },
+              color: selected ? colorScheme.secondary : colorScheme.background,
+              textColor: selected ? colorScheme.primary : colorScheme.secondary,
+            );
+          }).toList(),
+        ),
+        Divider(),
+      ],
+    );
+  }
+
+  Widget _bottomContent(BuildContext context) => Expanded(
+        child: currentPane.value == null
+            ? Offstage()
+            : currentPane.value.build(context),
+      );
+}
diff --git a/bin/terminal_settings/lib/src/widgets/settings_pane.dart b/bin/terminal_settings/lib/src/widgets/settings_pane.dart
new file mode 100644
index 0000000..5219953
--- /dev/null
+++ b/bin/terminal_settings/lib/src/widgets/settings_pane.dart
@@ -0,0 +1,27 @@
+// Copyright 2019 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';
+
+/// A pane to display in the settings container.
+abstract class SettingsPane extends StatelessWidget {
+  /// The title to display as the title of the button in the top bar
+  final String title;
+
+  const SettingsPane({this.title, Key key}) : super(key: key);
+}
+
+class ProfilesSetttingsPane extends SettingsPane {
+  const ProfilesSetttingsPane() : super(title: 'PROFILES');
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      color: Theme.of(context).colorScheme.background,
+      child: Center(
+        child: Text('Terminal Profiles'),
+      ),
+    );
+  }
+}
diff --git a/bin/terminal_settings/meta/terminal_settings.cmx b/bin/terminal_settings/meta/terminal_settings.cmx
new file mode 100644
index 0000000..d73a627
--- /dev/null
+++ b/bin/terminal_settings/meta/terminal_settings.cmx
@@ -0,0 +1,23 @@
+{
+    "program": {
+        "data": "data/terminal_settings"
+    },
+    "sandbox": {
+        "services": [
+            "fuchsia.cobalt.LoggerFactory",
+            "fuchsia.fonts.Provider",
+            "fuchsia.logger.LogSink",
+            "fuchsia.posix.socket.Provider",
+            "fuchsia.net.NameLookup",
+            "fuchsia.netstack.Netstack",
+            "fuchsia.process.Launcher",
+            "fuchsia.sys.Environment",
+            "fuchsia.sys.Launcher",
+            "fuchsia.ui.input.ImeService",
+            "fuchsia.ui.input.ImeVisibilityService",
+            "fuchsia.ui.scenic.Scenic",
+            "fuchsia.ui.policy.Presenter",
+            "fuchsia.web.ContextProvider"
+        ]
+    }
+}
diff --git a/bin/terminal_settings/pubspec.yaml b/bin/terminal_settings/pubspec.yaml
new file mode 100644
index 0000000..34a81eb
--- /dev/null
+++ b/bin/terminal_settings/pubspec.yaml
@@ -0,0 +1,8 @@
+# Copyright 2019 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.
+
+name: terminal_settings
+description: Module for updating the terminal settings
+
+flutter:
diff --git a/bin/terminal_settings/test/settings_container_test.dart b/bin/terminal_settings/test/settings_container_test.dart
new file mode 100644
index 0000000..68fdb64
--- /dev/null
+++ b/bin/terminal_settings/test/settings_container_test.dart
@@ -0,0 +1,89 @@
+// Copyright 2019 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:flutter_test/flutter_test.dart';
+
+// ignore_for_file: implementation_imports
+import 'package:terminal_settings/src/widgets/settings_container.dart';
+import 'package:terminal_settings/src/widgets/settings_pane.dart';
+
+void main() {
+  testWidgets('empty pane list does not fail', (tester) async {
+    final widget = _wrapInMaterialApp(SettingsContainer([]));
+    await tester.pumpWidget(widget);
+    // nothing here, test success is not crashing
+  });
+
+  testWidgets('creates button with title', (tester) async {
+    final widget = _wrapInMaterialApp(SettingsContainer([
+      _TestSettingsPane(title: 'foo'),
+    ]));
+
+    await tester.pumpWidget(widget);
+
+    expect(_findMaterialButtonWithText('foo'), findsOneWidget);
+  });
+
+  testWidgets('creates multiple buttons', (tester) async {
+    final widget = _wrapInMaterialApp(SettingsContainer([
+      _TestSettingsPane(title: 'foo'),
+      _TestSettingsPane(title: 'bar'),
+    ]));
+
+    await tester.pumpWidget(widget);
+
+    expect(_findMaterialButtonWithText('foo'), findsOneWidget);
+    expect(_findMaterialButtonWithText('bar'), findsOneWidget);
+  });
+
+  testWidgets('first pane is placed in body', (tester) async {
+    final widget = _wrapInMaterialApp(SettingsContainer([
+      _TestSettingsPane(title: 'foo', body: 'foo pane'),
+      _TestSettingsPane(title: 'bar', body: 'bar pane'),
+    ]));
+
+    await tester.pumpWidget(widget);
+
+    expect(find.text('foo pane'), findsOneWidget);
+  });
+
+  testWidgets('tapping button changes pane', (tester) async {
+    final widget = _wrapInMaterialApp(SettingsContainer([
+      _TestSettingsPane(title: 'foo', body: 'foo pane'),
+      _TestSettingsPane(title: 'bar', body: 'bar pane'),
+    ]));
+
+    await tester.pumpWidget(widget);
+    await tester.tap(_findMaterialButtonWithText('bar'));
+
+    await tester.pump();
+
+    expect(find.text('foo pane'), findsNothing);
+    expect(find.text('bar pane'), findsOneWidget);
+  });
+}
+
+Finder _findMaterialButtonWithText(String text) => find.ancestor(
+      of: find.text(text),
+      matching: find.byWidgetPredicate((w) => w is MaterialButton),
+    );
+
+// our widget needs to be in a material app to layout text.
+Widget _wrapInMaterialApp(Widget w) => MaterialApp(
+      // theme: ThemeData(primaryColor: Colors.yellow),
+      home: Scaffold(
+        body: w,
+      ),
+    );
+
+class _TestSettingsPane extends SettingsPane {
+  final String body;
+  const _TestSettingsPane({String title, this.body}) : super(title: title);
+
+  @override
+  Widget build(BuildContext context) {
+    return Text(body ?? '');
+  }
+}