diff options
author | Scott Murray <scott.murray@konsulko.com> | 2022-11-21 02:30:35 -0500 |
---|---|---|
committer | Scott Murray <scott.murray@konsulko.com> | 2022-11-21 02:44:25 -0500 |
commit | e21709c9601209e26d09dea0a45e37f0636bb605 (patch) | |
tree | 6f7e9d1bda9126cc0dfe6ba401b40739715867e2 /lib/page_apps.dart | |
parent | 1f5b482843291c17e3cbb265f59101f8d1874182 (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.dart | 159 |
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, + ), + ), + ), + ], + )); + } +} |