diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4ec35f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +format: + dart format $$(find . -type f -name '*.dart') +fix: + dart fix ./lib --apply diff --git a/lib/api/third_party/chartmuseum.dart b/lib/api/third_party/chartmuseum.dart new file mode 100644 index 0000000..696ee37 --- /dev/null +++ b/lib/api/third_party/chartmuseum.dart @@ -0,0 +1,33 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:dio/dio.dart'; + +class HelmChart { + final String name; + final String version; + + const HelmChart({ + required this.name, + required this.version, + }); +} + +Future> fetchCharts() async { + final dio = Dio(); + final response = await dio.get('https://helm.badhouseplants.net/api/charts', + options: Options(headers: { + "Accept": "application/json", + })); + if (response.statusCode == 200) { + final Map charsRaw = json.decode(response.data); + + List charts = []; + charsRaw.forEach((key, value) { + charts.add(HelmChart(name: key, version: value.first["version"])); + }); + return charts; + } else { + throw Exception('Failed to load album'); + } +} diff --git a/lib/components/catalog_card.dart b/lib/components/catalog_card.dart new file mode 100644 index 0000000..8f0469e --- /dev/null +++ b/lib/components/catalog_card.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:softplayer_web/models/catalog_entry.dart'; + +class CatalogCard extends StatelessWidget { + const CatalogCard({ + super.key, + required this.data, + }); + final List data; + + List createCards(List data) { + List createCards = []; + for (var app in data) { + createCards.add(Card( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + //leading: Image.network(app.logoUrl), + leading: const Icon(Icons.nfc), + title: Text(app.name), + subtitle: Text(app.description), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + child: const Text('INSTALL'), + onPressed: () { + print("installing"); + }, + ), + const SizedBox(width: 8), + ], + ), + ], + ), + )); + } + return createCards; + } + + @override + Widget build(BuildContext context) { + return Column( + children: createCards(data), + ); + } +} diff --git a/lib/components/menubar.dart b/lib/components/menubar.dart index 5dd5586..c7d86ee 100644 --- a/lib/components/menubar.dart +++ b/lib/components/menubar.dart @@ -2,14 +2,14 @@ import 'package:flutter/material.dart'; /// Flutter code sample for [AppBar]. -class MenuPanel extends StatefulWidget implements PreferredSizeWidget { +class MenuPanel extends StatefulWidget implements PreferredSizeWidget { final TabName tab; - MenuPanel({super.key, required this.tab}) : preferredSize = const Size.fromHeight(kToolbarHeight); + const MenuPanel({super.key, required this.tab}) + : preferredSize = const Size.fromHeight(kToolbarHeight); @override final Size preferredSize; // default is 56.0 @override State createState() => _MenuPanel(); - } enum TabName { home, catalog, about } @@ -22,40 +22,49 @@ class _MenuPanel extends State { PreferredSizeWidget build(BuildContext context) { final TabName tab = widget.tab; return AppBar( - title: Row( - children: [ + title: Row(children: [ TextButton( child: const Text("Softplayer"), onPressed: () { Navigator.pushNamed(context, "/"); }), - TextButton( - child: Text(linkHome, - style: (tab == TabName.home)? const TextStyle(decoration: TextDecoration.underline) : const TextStyle(), + TextButton( + child: Text( + linkHome, + style: (tab == TabName.home) + ? const TextStyle(decoration: TextDecoration.underline) + : const TextStyle(), ), onPressed: () { Navigator.pushNamed(context, "/"); }), - TextButton( - child: Text(linkCatalog, - style: (tab == TabName.catalog)? const TextStyle(decoration: TextDecoration.underline) : const TextStyle(), + TextButton( + child: Text( + linkCatalog, + style: (tab == TabName.catalog) + ? const TextStyle(decoration: TextDecoration.underline) + : const TextStyle(), ), onPressed: () { Navigator.pushNamed(context, "/catalog"); }), - TextButton( - child: Text(linkAbout, - style: (tab == TabName.about)? const TextStyle(decoration: TextDecoration.underline) : const TextStyle(), + TextButton( + child: Text( + linkAbout, + style: (tab == TabName.about) + ? const TextStyle(decoration: TextDecoration.underline) + : const TextStyle(), ), onPressed: () { Navigator.pushNamed(context, "/about"); }), - ]), - automaticallyImplyLeading: false, - actions: [ - IconButton(onPressed: () => print("acc"), icon: Icon(Icons.account_circle)) - ], - ); + ]), + automaticallyImplyLeading: false, + actions: [ + IconButton( + onPressed: () => print("acc"), icon: const Icon(Icons.account_circle)) + ], + backgroundColor: Colors.cyan, + ); } - } diff --git a/lib/main.dart b/lib/main.dart index 4099f0b..6e52176 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:softplayer_web/components/menubar.dart'; import 'package:softplayer_web/pages/about.dart'; import 'package:softplayer_web/pages/catalog.dart'; import 'package:softplayer_web/pages/home.dart'; @@ -40,9 +39,9 @@ class MyApp extends StatelessWidget { debugShowCheckedModeBanner: false, title: 'Flutter Demo', routes: { - '/': (context) => HomePage(), - '/catalog': (context) => CatalogPage(), - '/about': (context) => AboutPage(), + '/': (context) => const HomePage(), + '/catalog': (context) => const CatalogPage(), + '/about': (context) => const AboutPage(), }, theme: ThemeData( @@ -53,4 +52,3 @@ class MyApp extends StatelessWidget { ); } } - diff --git a/lib/models/catalog_entry.dart b/lib/models/catalog_entry.dart new file mode 100644 index 0000000..7c00629 --- /dev/null +++ b/lib/models/catalog_entry.dart @@ -0,0 +1,10 @@ +class CatalogEntry { + CatalogEntry({ + required this.name, + required this.description, + required this.logoUrl, + }); + final String name; + final String description; + final String logoUrl; +} diff --git a/lib/pages/about.dart b/lib/pages/about.dart index c5f9b9a..51c8e7e 100644 --- a/lib/pages/about.dart +++ b/lib/pages/about.dart @@ -13,7 +13,7 @@ class _AboutPage extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: MenuPanel(tab: TabName.about,), + appBar: const MenuPanel(tab: TabName.about), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/pages/catalog.dart b/lib/pages/catalog.dart index 222d192..56ee745 100644 --- a/lib/pages/catalog.dart +++ b/lib/pages/catalog.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:softplayer_web/api/third_party/chartmuseum.dart'; +import 'package:softplayer_web/components/catalog_card.dart'; import 'package:softplayer_web/components/menubar.dart'; +import 'package:softplayer_web/models/catalog_entry.dart'; class CatalogPage extends StatefulWidget { const CatalogPage({super.key}); @@ -10,36 +13,68 @@ class CatalogPage extends StatefulWidget { } class _CatalogPage extends State { - int _counter = 0; - void _incrementCounter() { - setState(() { - _counter++; - }); + late Future> helmChart; + @override + void initState() { + super.initState(); + helmChart = fetchCharts(); } + final List catalog = [ + CatalogEntry( + name: "openvpn", + description: "you know what I mean", + logoUrl: + "https://upload.wikimedia.org/wikipedia/commons/f/f5/OpenVPN_logo.svg"), + CatalogEntry( + name: "openvpn", + description: "you know what I mean", + logoUrl: + "https://upload.wikimedia.org/wikipedia/commons/f/f5/OpenVPN_logo.svg"), + ]; @override Widget build(BuildContext context) { - return Scaffold( - appBar: MenuPanel(tab: TabName.catalog), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', + print(helmChart); + return SelectionArea( + child: Scaffold( + appBar: const MenuPanel(tab: TabName.catalog), + body: Container( + margin: const EdgeInsets.all(14), + child: Container( + child: Row(children: [ + const SizedBox( + width: 200, + child: Card( + child: Column( + children: [Text("Filter")], + ))), + Flexible( + child: Column( + children: [ + const TextField( + decoration: InputDecoration( + icon: Icon(Icons.search), + labelText: "Search", + ), + autofocus: true, + ), + CatalogCard(data: catalog), + FutureBuilder( + future: helmChart, + builder: (context, snapshot) { + print(snapshot); + if (snapshot.hasData) { + return Text(snapshot.data!.first.name); + } else if (snapshot.hasError) { + return SelectableText('${snapshot.error}'); + } + return const CircularProgressIndicator(); + }), + ], ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), + ) + ])), ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment v rot', - child: const Icon(Icons.hd), - ), - ); + )); } } diff --git a/lib/pages/home.dart b/lib/pages/home.dart index acb1951..de01fe4 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -20,7 +20,7 @@ class _HomePage extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: MenuPanel(tab: TabName.home), + appBar: const MenuPanel(tab: TabName.home), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/pubspec.lock b/pubspec.lock index 9371436..a96f994 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dio: + dependency: "direct main" + description: + name: dio + sha256: "50fec96118958b97c727d0d8f67255d3683f16cc1f90d9bc917b5d4fe3abeca9" + url: "https://pub.dev" + source: hosted + version: "5.4.2" fake_async: dependency: transitive description: @@ -140,7 +148,7 @@ packages: source: hosted version: "3.2.4" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" diff --git a/pubspec.yaml b/pubspec.yaml index 93a8b86..aa1185a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,8 @@ dependencies: cupertino_icons: ^1.0.6 grpc: ^3.2.4 + http: ^1.2.1 + dio: ^5.4.2 dev_dependencies: flutter_test: