Start developping the catalog

This commit is contained in:
Nikolai Rodionov 2024-03-27 22:07:53 +01:00
parent d5b2ef18ab
commit 3cc8edfc8c
Signed by: allanger
GPG Key ID: 0AA46A90E25592AD
11 changed files with 202 additions and 54 deletions

4
Makefile Normal file
View File

@ -0,0 +1,4 @@
format:
dart format $$(find . -type f -name '*.dart')
fix:
dart fix ./lib --apply

33
lib/api/third_party/chartmuseum.dart vendored Normal file
View File

@ -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<List<HelmChart>> 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<dynamic, dynamic> charsRaw = json.decode(response.data);
List<HelmChart> charts = [];
charsRaw.forEach((key, value) {
charts.add(HelmChart(name: key, version: value.first["version"]));
});
return charts;
} else {
throw Exception('Failed to load album');
}
}

View File

@ -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<CatalogEntry> data;
List<Widget> createCards(List<CatalogEntry> data) {
List<Widget> createCards = [];
for (var app in data) {
createCards.add(Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
//leading: Image.network(app.logoUrl),
leading: const Icon(Icons.nfc),
title: Text(app.name),
subtitle: Text(app.description),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
child: const Text('INSTALL'),
onPressed: () {
print("installing");
},
),
const SizedBox(width: 8),
],
),
],
),
));
}
return createCards;
}
@override
Widget build(BuildContext context) {
return Column(
children: createCards(data),
);
}
}

View File

@ -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<StatefulWidget> createState() => _MenuPanel();
}
enum TabName { home, catalog, about }
@ -22,40 +22,49 @@ class _MenuPanel extends State<MenuPanel> {
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: <Widget>[
IconButton(onPressed: () => print("acc"), icon: Icon(Icons.account_circle))
],
);
]),
automaticallyImplyLeading: false,
actions: <Widget>[
IconButton(
onPressed: () => print("acc"), icon: const Icon(Icons.account_circle))
],
backgroundColor: Colors.cyan,
);
}
}

View File

@ -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 {
);
}
}

View File

@ -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;
}

View File

@ -13,7 +13,7 @@ class _AboutPage extends State<AboutPage> {
@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,

View File

@ -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<CatalogPage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
late Future<List<HelmChart>> helmChart;
@override
void initState() {
super.initState();
helmChart = fetchCharts();
}
final List<CatalogEntry> 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: <Widget>[
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: <Widget>[
const SizedBox(
width: 200,
child: Card(
child: Column(
children: <Widget>[Text("Filter")],
))),
Flexible(
child: Column(
children: <Widget>[
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),
),
);
));
}
}

View File

@ -20,7 +20,7 @@ class _HomePage extends State<HomePage> {
@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,

View File

@ -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"

View File

@ -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: