summaryrefslogtreecommitdiffstats
path: root/lib/page_apps.dart
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2022-11-21 02:30:35 -0500
committerScott Murray <scott.murray@konsulko.com>2022-11-21 02:44:25 -0500
commite21709c9601209e26d09dea0a45e37f0636bb605 (patch)
tree6f7e9d1bda9126cc0dfe6ba401b40739715867e2 /lib/page_apps.dart
parent1f5b482843291c17e3cbb265f59101f8d1874182 (diff)
Rework for use in Flutter demo platform image
Changes: - Converted to portrait orientation. - Application enumeration and launching+activation enabled via use of applaunchd gRPC API and agl-shell protocol platform channel plugin in the embedder. - Previous dashboard, hvac, media, etc. pages disabled. Some of the code has been kept for potential reuse. - Clock widget tweaked to fit in portrait mode navigation bar, and take text color argument. - Bluetooth, wifi, and phone signal icons mocked up in navigation bar. Known issues: - The bottom panel area is static at present, support for popping up a volume control slider like the Qt demo is planned as an addition. - The path to implementing connection and signal strength indications is currently a bit hazy, it is possible that flutter-dbus might be the simplest stopgap. - State management has been kept basic, as there are a couple of places where using provider or riverpod seems like perhaps an overcomplication. This will be reviewed when KUKSA.val support is integrated for the volume slider. - Some of the layout sizing is a bit ad hoc, and it is not clear if the previous layout helper class is actually worth keeping or not. This should be reviewed when time permits. Bug-AGL: SPEC-4611 Signed-off-by: Scott Murray <scott.murray@konsulko.com> Change-Id: Ib486b1fd92047f6c1ff1cd9569f49e3ccaf3269d
Diffstat (limited to 'lib/page_apps.dart')
-rw-r--r--lib/page_apps.dart159
1 files changed, 159 insertions, 0 deletions
diff --git a/lib/page_apps.dart b/lib/page_apps.dart
new file mode 100644
index 0000000..39fb754
--- /dev/null
+++ b/lib/page_apps.dart
@@ -0,0 +1,159 @@
+import 'dart:io';
+import 'dart:convert';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:jovial_svg/jovial_svg.dart';
+import 'package:flutter_homescreen/homescreen.dart';
+import 'package:flutter_homescreen/layout_size_helper.dart';
+import 'package:flutter_homescreen/generated/applauncher.pb.dart';
+
+// The Applications page.
+class AppsPage extends StatefulWidget {
+ final Future<List<AppInfo>> Function() getApps;
+ final Function(String id) startApp;
+
+ const AppsPage({Key? key, required this.getApps, required this.startApp})
+ : super(key: key);
+
+ @override
+ _AppsPageState createState() => _AppsPageState();
+}
+
+class _AppsPageState extends State<AppsPage> {
+ List<AppInfo> apps = [];
+
+ @override
+ initState() {
+ widget.getApps().then((val) => setState(() {
+ apps = val;
+ }));
+
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ var sizeHelper = LayoutSizeHelper(context);
+ return Container(
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topRight,
+ end: Alignment.bottomLeft,
+ colors: [Colors.blueGrey.shade800, Colors.grey.shade900])),
+ constraints: BoxConstraints.expand(),
+ alignment: Alignment.center,
+ child: Column(children: [
+ SizedBox(height: 160.0),
+ GridView.builder(
+ scrollDirection: Axis.vertical,
+ shrinkWrap: true,
+ itemCount: apps.length,
+ gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: 3),
+ itemBuilder: (context, index) {
+ return GridTile(
+ child: Container(
+ alignment: Alignment.center,
+ child: _AppsPageEntry(
+ id: apps[index].id,
+ label: apps[index].name,
+ iconPath: apps[index].iconPath,
+ appSelected: widget.startApp,
+ )));
+ })
+ ]),
+ );
+ }
+}
+
+// Each one of the items on the Home page.
+class _AppsPageEntry extends StatefulWidget {
+ final String id;
+ final String label;
+ final String iconPath;
+ final void Function(String id) appSelected;
+
+ const _AppsPageEntry(
+ {Key? key,
+ required this.id,
+ required this.label,
+ required this.iconPath,
+ required this.appSelected})
+ : super(key: key);
+
+ @override
+ _AppsPageEntryState createState() => _AppsPageEntryState();
+}
+
+class _AppsPageEntryState extends State<_AppsPageEntry> {
+ late ScalableImage svgIcon;
+ bool svgIconLoaded = false;
+ final iconColor = const Color(0xff4ee6f5);
+
+ @override
+ void initState() {
+ if (widget.iconPath.endsWith(".svg")) {
+ readSvgIcon().then((val) => setState(() {
+ svgIconLoaded = val;
+ }));
+ }
+ super.initState();
+ }
+
+ Future<bool> readSvgIcon() async {
+ if (widget.iconPath.endsWith(".svg")) {
+ var iconFile = File(widget.iconPath);
+ if (await iconFile.exists()) {
+ svgIcon = await ScalableImage.fromSvgStream(iconFile.openRead().transform(utf8.decoder));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Widget buildIcon() {
+ if (svgIconLoaded) {
+ return GestureDetector(
+ onTap: () {
+ widget.appSelected(widget.id);
+ },
+ child: SizedBox.expand(
+ child: ScalableImageWidget(si: svgIcon))
+ );
+ } else {
+ return OutlinedButton(
+ style: ElevatedButton.styleFrom(
+ shape: CircleBorder(),
+ padding: EdgeInsets.all(8),
+ side: BorderSide(width: 4, color: iconColor),
+ ),
+ onPressed: () {
+ widget.appSelected(widget.id);
+ },
+ child: Icon(Icons.question_mark,
+ color: iconColor,
+ size: 160.0));
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 32.0),
+ child: Column(
+ children: [
+ Expanded(child: buildIcon()),
+ Padding(
+ padding: EdgeInsets.all(4),
+ child: Text(
+ widget.label,
+ style: DefaultTextStyle.of(context).style.copyWith(
+ fontSize: 28,
+ color: iconColor,
+ ),
+ ),
+ ),
+ ],
+ ));
+ }
+}