Skip to content

Commit 67eb30d

Browse files
author
Lukas Gundermann
committed
feat: Add "Additional Resources" section to home page
This commit introduces a new "Additional Resources" section to the home screen, providing users with quick access to key project links. It also includes a minor refactor of the existing "Technology Stack" widget for better naming consistency. ### Key Changes: * **New `AdditionalResources` Widget (`lib/screens/home/widgets/additional_resources.dart`):** * A new widget has been created to display a grid of resource cards. * It includes links to the Documentation, GitHub Repository, and a Live Demo. * Each card features an icon, title, description, and a clickable link that opens in an external application. * The section is styled with a distinct background color that adapts to the current theme. * **Home Screen Update (`lib/screens/home/home_screen.dart`):** * The `AdditionalResources` widget is now included in the main column layout of the `HomeScreen`. * **Refactor `TechnologyStack` (`lib/screens/home/widgets/technology_stack.dart`):** * The internal variable `_heroCards` has been renamed to `_stackCards` for clarity and consistency. * Associated comments have been updated to reflect the new naming.
1 parent a46f1f4 commit 67eb30d

3 files changed

Lines changed: 177 additions & 8 deletions

File tree

lib/screens/home/home_screen.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:police_flutter_template/screens/home/widgets/additional_resources.dart';
12
import 'package:police_flutter_template/screens/home/widgets/hero_section.dart';
23
import 'package:police_flutter_template/screens/home/widgets/installation.dart';
34
import 'package:police_flutter_template/screens/home/widgets/technology_stack.dart';
@@ -10,7 +11,12 @@ class HomeScreen extends StatelessWidget {
1011
Widget build(BuildContext context) {
1112
return Column(
1213
mainAxisSize: MainAxisSize.max,
13-
children: [HeroSection(), Installation(), TechnologyStack()],
14+
children: [
15+
HeroSection(),
16+
Installation(),
17+
TechnologyStack(),
18+
AdditionalResources(),
19+
],
1420
);
1521
}
1622
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import 'package:flutter_bloc/flutter_bloc.dart';
2+
import 'package:shadcn_flutter/shadcn_flutter.dart';
3+
import 'package:url_launcher/url_launcher.dart';
4+
5+
import '../../../extensions/text_extensions.dart';
6+
import '../../../theme/cubit/theme_cubit.dart';
7+
8+
class AdditionalResources extends StatelessWidget {
9+
const AdditionalResources({super.key});
10+
11+
/// Static list of resource cards displayed in the resource grid.
12+
final List<_ResourceCard> _resourceCards = const [
13+
_ResourceCard(
14+
icon: LucideIcons.book,
15+
title: "Dokumentation",
16+
text: "Vollständige Dokumentation aller Komponenten und Features",
17+
linkText: "Dokumentation öffnen",
18+
link: "https://sunarya-thito.github.io/shadcn_flutter/",
19+
),
20+
_ResourceCard(
21+
icon: LucideIcons.code,
22+
title: "GitHub Repository",
23+
text: "Quellcode, Issues und Beiträge auf GitHub",
24+
linkText: "Zu Github",
25+
link: "https://github.com/First-Coder/Polizei-Flutter-Template",
26+
),
27+
_ResourceCard(
28+
icon: LucideIcons.eye,
29+
title: "Live Demo",
30+
text: "Live Demo der Applikation",
31+
linkText: "Live Demo öffnen",
32+
link: "https://first-coder.github.io/Polizei-Flutter-Template/#/home",
33+
),
34+
];
35+
36+
@override
37+
Widget build(BuildContext context) {
38+
final isDarkMode = context.watch<ThemeCubit>().state.isDarkMode;
39+
return Container(
40+
width: double.infinity,
41+
color: isDarkMode ? Colors.gray[950] : Colors.gray[50],
42+
child: Column(
43+
children: [
44+
Text('Technologie Stack').bold.responsive(
45+
context,
46+
mobile: (t) => t.x3Large,
47+
tablet: (t) => t.x3Large,
48+
desktop: (t) => t.x4Large,
49+
),
50+
Gap(16),
51+
Text(
52+
'Moderne und bewährte Technologien für beste Performance',
53+
).setColors(
54+
lightColor: Colors.gray[600],
55+
darkColor: Colors.gray[400],
56+
),
57+
Gap(48),
58+
Container(
59+
constraints: const BoxConstraints(maxWidth: 1000),
60+
padding: const EdgeInsets.symmetric(horizontal: 16),
61+
child: LayoutBuilder(
62+
builder: (context, constraints) {
63+
const spacing = 16.0;
64+
const minCardWidth = 200.0;
65+
const maxColumns = 3;
66+
67+
final availableWidth = constraints.maxWidth;
68+
69+
final columns =
70+
((availableWidth + spacing) / (minCardWidth + spacing))
71+
.floor()
72+
.clamp(1, maxColumns);
73+
74+
return GridView.builder(
75+
shrinkWrap: true,
76+
physics: const NeverScrollableScrollPhysics(),
77+
itemCount: _resourceCards.length,
78+
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
79+
crossAxisCount: columns,
80+
crossAxisSpacing: spacing,
81+
mainAxisSpacing: spacing,
82+
mainAxisExtent: 210, // Breite/Höhe der Cards
83+
),
84+
itemBuilder: (context, index) {
85+
final card = _resourceCards[index];
86+
return Card(
87+
child: Padding(
88+
padding: const EdgeInsets.all(10),
89+
child: Column(
90+
crossAxisAlignment: CrossAxisAlignment.start,
91+
children: [
92+
Icon(
93+
card.icon,
94+
size: 32,
95+
color: isDarkMode
96+
? Colors.blue[400]
97+
: Colors.blue[900],
98+
),
99+
const Gap(16),
100+
Text(card.title).h4,
101+
const Gap(6),
102+
Flexible(
103+
child: Text(
104+
card.text,
105+
maxLines: 3,
106+
overflow: TextOverflow.ellipsis,
107+
).muted.small,
108+
),
109+
const Gap(12),
110+
LinkButton(
111+
density: ButtonDensity.compact,
112+
child: Text(card.linkText),
113+
onPressed: () async {
114+
final uri = Uri.parse(card.link);
115+
if (!await launchUrl(
116+
uri,
117+
mode: LaunchMode.externalApplication,
118+
)) {
119+
// TODO: Handle error with notification
120+
// throw Exception('Konnte URL nicht öffnen: $uri');
121+
}
122+
},
123+
),
124+
],
125+
),
126+
),
127+
);
128+
},
129+
);
130+
},
131+
),
132+
),
133+
],
134+
).withPadding(vertical: 64),
135+
);
136+
}
137+
}
138+
139+
/// Immutable data object used to render a ressource card.
140+
class _ResourceCard {
141+
const _ResourceCard({
142+
required this.icon,
143+
required this.title,
144+
required this.text,
145+
required this.linkText,
146+
required this.link,
147+
});
148+
149+
/// Ressource icon.
150+
final IconData icon;
151+
152+
/// Ressource title.
153+
final String title;
154+
155+
/// Short ressource description.
156+
final String text;
157+
158+
/// Ressource link text.
159+
final String linkText;
160+
161+
/// Ressource link.
162+
final String link;
163+
}

lib/screens/home/widgets/technology_stack.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import '../../../theme/cubit/theme_cubit.dart';
77
class TechnologyStack extends StatelessWidget {
88
const TechnologyStack({super.key});
99

10-
/// Static list of feature cards displayed in the hero grid.
11-
final List<_StackCard> _heroCards = const [
10+
/// Static list of stack cards displayed in the stack grid.
11+
final List<_StackCard> _stackCards = const [
1212
_StackCard(title: "Flutter", text: "aktueller Stable-Channel empfohlen"),
1313
_StackCard(title: "State Management", text: "flutter_bloc, equatable"),
1414
_StackCard(title: "Routing", text: "go_router"),
@@ -66,15 +66,15 @@ class TechnologyStack extends StatelessWidget {
6666
return GridView.builder(
6767
shrinkWrap: true,
6868
physics: const NeverScrollableScrollPhysics(),
69-
itemCount: _heroCards.length,
69+
itemCount: _stackCards.length,
7070
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
7171
crossAxisCount: columns,
7272
crossAxisSpacing: spacing,
7373
mainAxisSpacing: spacing,
7474
mainAxisExtent: 210, // Breite/Höhe der Cards
7575
),
7676
itemBuilder: (context, index) {
77-
final card = _heroCards[index];
77+
final card = _stackCards[index];
7878
return Card(
7979
child: Padding(
8080
padding: const EdgeInsets.all(10),
@@ -114,13 +114,13 @@ class TechnologyStack extends StatelessWidget {
114114
}
115115
}
116116

117-
/// Immutable data object used to render a single hero feature card.
117+
/// Immutable data object used to render a stack card.
118118
class _StackCard {
119119
const _StackCard({required this.title, required this.text});
120120

121-
/// Feature title.
121+
/// Stack title.
122122
final String title;
123123

124-
/// Short feature description.
124+
/// Short stack description.
125125
final String text;
126126
}

0 commit comments

Comments
 (0)