Start developping the catalog
This commit is contained in:
parent
d5b2ef18ab
commit
3cc8edfc8c
4
Makefile
Normal file
4
Makefile
Normal 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
33
lib/api/third_party/chartmuseum.dart
vendored
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
49
lib/components/catalog_card.dart
Normal file
49
lib/components/catalog_card.dart
Normal 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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
/// Flutter code sample for [AppBar].
|
/// Flutter code sample for [AppBar].
|
||||||
|
|
||||||
class MenuPanel extends StatefulWidget implements PreferredSizeWidget {
|
class MenuPanel extends StatefulWidget implements PreferredSizeWidget {
|
||||||
final TabName tab;
|
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
|
@override
|
||||||
final Size preferredSize; // default is 56.0
|
final Size preferredSize; // default is 56.0
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _MenuPanel();
|
State<StatefulWidget> createState() => _MenuPanel();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TabName { home, catalog, about }
|
enum TabName { home, catalog, about }
|
||||||
@ -22,40 +22,49 @@ class _MenuPanel extends State<MenuPanel> {
|
|||||||
PreferredSizeWidget build(BuildContext context) {
|
PreferredSizeWidget build(BuildContext context) {
|
||||||
final TabName tab = widget.tab;
|
final TabName tab = widget.tab;
|
||||||
return AppBar(
|
return AppBar(
|
||||||
title: Row(
|
title: Row(children: [
|
||||||
children: [
|
|
||||||
TextButton(
|
TextButton(
|
||||||
child: const Text("Softplayer"),
|
child: const Text("Softplayer"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pushNamed(context, "/");
|
Navigator.pushNamed(context, "/");
|
||||||
}),
|
}),
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(linkHome,
|
child: Text(
|
||||||
style: (tab == TabName.home)? const TextStyle(decoration: TextDecoration.underline) : const TextStyle(),
|
linkHome,
|
||||||
|
style: (tab == TabName.home)
|
||||||
|
? const TextStyle(decoration: TextDecoration.underline)
|
||||||
|
: const TextStyle(),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pushNamed(context, "/");
|
Navigator.pushNamed(context, "/");
|
||||||
}),
|
}),
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(linkCatalog,
|
child: Text(
|
||||||
style: (tab == TabName.catalog)? const TextStyle(decoration: TextDecoration.underline) : const TextStyle(),
|
linkCatalog,
|
||||||
|
style: (tab == TabName.catalog)
|
||||||
|
? const TextStyle(decoration: TextDecoration.underline)
|
||||||
|
: const TextStyle(),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pushNamed(context, "/catalog");
|
Navigator.pushNamed(context, "/catalog");
|
||||||
}),
|
}),
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(linkAbout,
|
child: Text(
|
||||||
style: (tab == TabName.about)? const TextStyle(decoration: TextDecoration.underline) : const TextStyle(),
|
linkAbout,
|
||||||
|
style: (tab == TabName.about)
|
||||||
|
? const TextStyle(decoration: TextDecoration.underline)
|
||||||
|
: const TextStyle(),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pushNamed(context, "/about");
|
Navigator.pushNamed(context, "/about");
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(onPressed: () => print("acc"), icon: Icon(Icons.account_circle))
|
IconButton(
|
||||||
],
|
onPressed: () => print("acc"), icon: const Icon(Icons.account_circle))
|
||||||
);
|
],
|
||||||
|
backgroundColor: Colors.cyan,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:softplayer_web/components/menubar.dart';
|
|
||||||
import 'package:softplayer_web/pages/about.dart';
|
import 'package:softplayer_web/pages/about.dart';
|
||||||
import 'package:softplayer_web/pages/catalog.dart';
|
import 'package:softplayer_web/pages/catalog.dart';
|
||||||
import 'package:softplayer_web/pages/home.dart';
|
import 'package:softplayer_web/pages/home.dart';
|
||||||
@ -40,9 +39,9 @@ class MyApp extends StatelessWidget {
|
|||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: 'Flutter Demo',
|
title: 'Flutter Demo',
|
||||||
routes: {
|
routes: {
|
||||||
'/': (context) => HomePage(),
|
'/': (context) => const HomePage(),
|
||||||
'/catalog': (context) => CatalogPage(),
|
'/catalog': (context) => const CatalogPage(),
|
||||||
'/about': (context) => AboutPage(),
|
'/about': (context) => const AboutPage(),
|
||||||
},
|
},
|
||||||
|
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
@ -53,4 +52,3 @@ class MyApp extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
lib/models/catalog_entry.dart
Normal file
10
lib/models/catalog_entry.dart
Normal 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;
|
||||||
|
}
|
@ -13,7 +13,7 @@ class _AboutPage extends State<AboutPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: MenuPanel(tab: TabName.about,),
|
appBar: const MenuPanel(tab: TabName.about),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
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/components/menubar.dart';
|
||||||
|
import 'package:softplayer_web/models/catalog_entry.dart';
|
||||||
|
|
||||||
class CatalogPage extends StatefulWidget {
|
class CatalogPage extends StatefulWidget {
|
||||||
const CatalogPage({super.key});
|
const CatalogPage({super.key});
|
||||||
@ -10,36 +13,68 @@ class CatalogPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CatalogPage extends State<CatalogPage> {
|
class _CatalogPage extends State<CatalogPage> {
|
||||||
int _counter = 0;
|
late Future<List<HelmChart>> helmChart;
|
||||||
void _incrementCounter() {
|
@override
|
||||||
setState(() {
|
void initState() {
|
||||||
_counter++;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
print(helmChart);
|
||||||
appBar: MenuPanel(tab: TabName.catalog),
|
return SelectionArea(
|
||||||
body: Center(
|
child: Scaffold(
|
||||||
child: Column(
|
appBar: const MenuPanel(tab: TabName.catalog),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
body: Container(
|
||||||
children: <Widget>[
|
margin: const EdgeInsets.all(14),
|
||||||
const Text(
|
child: Container(
|
||||||
'You have pushed the button this many times:',
|
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),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class _HomePage extends State<HomePage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: MenuPanel(tab: TabName.home),
|
appBar: const MenuPanel(tab: TabName.home),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
10
pubspec.lock
10
pubspec.lock
@ -81,6 +81,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
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:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -140,7 +148,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.4"
|
version: "3.2.4"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
||||||
|
@ -17,6 +17,8 @@ dependencies:
|
|||||||
|
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
grpc: ^3.2.4
|
grpc: ^3.2.4
|
||||||
|
http: ^1.2.1
|
||||||
|
dio: ^5.4.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user