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].
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MenuPanel(tab: TabName.about,),
|
||||
appBar: const MenuPanel(tab: TabName.about),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
@ -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),
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
10
pubspec.lock
10
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"
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user