Skip to content

Commit 1fdcc32

Browse files
author
Lukas Gundermann
committed
feat: Add Installation section to home screen
This commit introduces a new "Installation" section on the home screen, providing users with initial setup instructions. It also includes a reusable `CodeCard` widget for displaying code snippets and updates the color theme. ### Key Changes: * **`HomeScreen` Update:** * The `HomeScreen` now includes a new `Installation` widget, displayed below the hero section, to guide users on how to clone and set up the project. * **New `Installation` Widget (`lib/screens/home/widgets/installation.dart`):** * A new widget that displays a title, a subtitle, and a `CodeCard` containing the `git clone` command. * Its background color adapts to the current theme (dark/light mode). * **New `CodeCard` Widget (`lib/screens/widgets/code_card.dart`):** * A reusable card component designed to display code snippets. * Features a header with a title and icon, a "copy" button to copy the code to the clipboard, and a themed container. * Supports different line types (`CodeTextLine`, `CodeCommentLine`, `CodeWidgetLine`) for flexible content rendering. * **Theme and Dependency Updates:** * The `shadcn_flutter` package is updated from version `^0.0.47` to `^0.0.49`. * The primary color scheme for both light and dark themes has been refined using predefined palettes from `ColorSchemes` (`lightSlate.blue` and `darkSlate.blue`) for better visual consistency.
1 parent 1848690 commit 1fdcc32

6 files changed

Lines changed: 273 additions & 7 deletions

File tree

lib/screens/home/home_screen.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import 'package:police_flutter_template/screens/home/widgets/hero_section.dart';
2+
import 'package:police_flutter_template/screens/home/widgets/installation.dart';
23
import 'package:shadcn_flutter/shadcn_flutter.dart';
34

45
class HomeScreen extends StatelessWidget {
56
const HomeScreen({super.key});
67

78
@override
89
Widget build(BuildContext context) {
9-
return Column(mainAxisSize: MainAxisSize.max, children: [HeroSection()]);
10+
return Column(
11+
mainAxisSize: MainAxisSize.max,
12+
children: [HeroSection(), Installation(), Gap(29)],
13+
);
1014
}
1115
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'package:flutter_bloc/flutter_bloc.dart';
2+
import 'package:police_flutter_template/extensions/text_extensions.dart';
3+
import 'package:police_flutter_template/screens/widgets/code_card.dart';
4+
import 'package:shadcn_flutter/shadcn_flutter.dart';
5+
6+
import '../../../theme/cubit/theme_cubit.dart';
7+
8+
class Installation extends StatelessWidget {
9+
const Installation({super.key});
10+
11+
@override
12+
Widget build(BuildContext context) {
13+
final isDarkMode = context.watch<ThemeCubit>().state.isDarkMode;
14+
return Container(
15+
width: double.infinity,
16+
color: isDarkMode ? Colors.gray[900] : Colors.white,
17+
child: Column(
18+
children: [
19+
Text('Installation').bold.responsive(
20+
context,
21+
mobile: (t) => t.x3Large,
22+
tablet: (t) => t.x3Large,
23+
desktop: (t) => t.x4Large,
24+
),
25+
Gap(16),
26+
Text(
27+
'Starten Sie in wenigen Minuten mit dem Flutter Template',
28+
).setColors(
29+
lightColor: Colors.gray[600],
30+
darkColor: Colors.gray[400],
31+
),
32+
Gap(48),
33+
ConstrainedBox(
34+
constraints: BoxConstraints(maxWidth: 900),
35+
child: CodeCard(
36+
title: 'Installation in flutter',
37+
lines: [
38+
CodeCommentLine("# Repository klonen"),
39+
CodeTextLine(
40+
"git clone https://github.com/First-Coder/Polizei-Flutter-Template.git",
41+
),
42+
CodeTextLine("cd police_flutter_template"),
43+
],
44+
),
45+
).withPadding(horizontal: 20),
46+
],
47+
).withPadding(vertical: 64),
48+
);
49+
}
50+
}

lib/screens/widgets/code_card.dart

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import 'package:flutter/services.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:shadcn_flutter/shadcn_flutter.dart';
4+
5+
import '../../extensions/text_extensions.dart';
6+
import '../../theme/cubit/theme_cubit.dart';
7+
8+
/// Represents a single renderable line inside a [CodeCard].
9+
///
10+
/// Use one of the concrete subclasses to describe what should be displayed:
11+
/// - [CodeTextLine] for regular code text (usually rendered in a monospace style)
12+
/// - [CodeCommentLine] for comment text (usually rendered muted)
13+
/// - [CodeWidgetLine] for embedding an arbitrary widget (e.g. a button, badge, input)
14+
///
15+
/// This sealed hierarchy also allows [CodeCard] to build a “copy to clipboard”
16+
/// string by extracting text from supported line types.
17+
sealed class CodeLine {
18+
/// Base constructor for all code line types.
19+
const CodeLine();
20+
}
21+
22+
/// A plain text line that is rendered as code.
23+
///
24+
/// This line is included in the copied-to-clipboard output.
25+
class CodeTextLine extends CodeLine {
26+
/// Creates a text line containing [text].
27+
const CodeTextLine(this.text);
28+
29+
/// The code text to render and include in the clipboard copy.
30+
final String text;
31+
}
32+
33+
/// A comment line (e.g. `# ...` or `// ...`) that is rendered in a muted style.
34+
///
35+
/// This line is included in the copied-to-clipboard output.
36+
class CodeCommentLine extends CodeLine {
37+
/// Creates a comment line containing [text].
38+
const CodeCommentLine(this.text);
39+
40+
/// The comment text to render and include in the clipboard copy.
41+
final String text;
42+
}
43+
44+
/// A line that renders a custom [widget] inside the code block.
45+
///
46+
/// By default, widget lines are *not* included in the copied output.
47+
/// If you want a widget line to contribute text to the clipboard copy,
48+
/// provide [includeInCopyAs].
49+
class CodeWidgetLine extends CodeLine {
50+
/// Creates a widget line.
51+
///
52+
/// - [widget] is rendered visually inside the code area.
53+
/// - [includeInCopyAs] (optional) is appended to the copied code string.
54+
const CodeWidgetLine(this.widget, {this.includeInCopyAs});
55+
56+
/// The widget to render in place of a text line.
57+
final Widget widget;
58+
59+
/// Optional text representation used when the user copies the code.
60+
///
61+
/// If null, this line will be omitted from the clipboard output.
62+
final String? includeInCopyAs;
63+
}
64+
65+
/// A card widget that displays a titled code snippet with a “copy” action.
66+
///
67+
/// [CodeCard] renders:
68+
/// - a header row with a terminal icon and [title]
69+
/// - an optional [description]
70+
/// - a code-like container that lists [lines]
71+
/// - a copy button that puts the assembled code text onto the clipboard
72+
///
73+
/// ## Clipboard behavior
74+
/// The copied string is produced by iterating over [lines]:
75+
/// - [CodeTextLine.text] is included
76+
/// - [CodeCommentLine.text] is included
77+
/// - [CodeWidgetLine.includeInCopyAs] is included if not null
78+
/// Each included line is joined with `\n`.
79+
///
80+
/// ## Theming
81+
/// The component adapts its colors based on the app's theme state
82+
/// (e.g. dark mode), typically read from a [ThemeCubit].
83+
///
84+
/// ## Example
85+
/// ```dart
86+
/// CodeCard(
87+
/// title: 'Installation',
88+
/// description: 'Get started in minutes.',
89+
/// lines: const [
90+
/// CodeCommentLine('# Clone the repository'),
91+
/// CodeTextLine('git clone <REPO_URL>'),
92+
/// CodeTextLine('cd my_project'),
93+
/// ],
94+
/// )
95+
/// ```
96+
class CodeCard extends StatelessWidget {
97+
const CodeCard({
98+
super.key,
99+
required this.title,
100+
this.description,
101+
required this.lines,
102+
});
103+
104+
/// The title shown in the header row.
105+
final String title;
106+
107+
/// Optional supporting text shown below the title.
108+
final String? description;
109+
110+
/// The content lines rendered inside the code container.
111+
///
112+
/// Use [CodeTextLine], [CodeCommentLine], and [CodeWidgetLine].
113+
final List<CodeLine> lines;
114+
115+
@override
116+
Widget build(BuildContext context) {
117+
final isDarkMode = context.watch<ThemeCubit>().state.isDarkMode;
118+
119+
final codeText = lines
120+
.map((line) {
121+
if (line is CodeTextLine) return line.text;
122+
if (line is CodeCommentLine) return line.text;
123+
if (line is CodeWidgetLine) return line.includeInCopyAs;
124+
return null;
125+
})
126+
.whereType<String>()
127+
.join('\n');
128+
129+
return Card(
130+
filled: true,
131+
fillColor: isDarkMode ? Colors.gray[800] : Colors.transparent,
132+
borderColor: isDarkMode ? Colors.gray[700] : Colors.gray[300],
133+
padding: EdgeInsets.all(20),
134+
child: Column(
135+
mainAxisSize: MainAxisSize.min,
136+
crossAxisAlignment: CrossAxisAlignment.start,
137+
children: [
138+
Row(
139+
children: [const Icon(LucideIcons.terminal), Text(title).semiBold],
140+
).gap(10),
141+
if (description != null) ...[Gap(10), Text(description!).muted],
142+
Gap(24),
143+
Container(
144+
width: double.infinity,
145+
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 30),
146+
decoration: BoxDecoration(
147+
color: isDarkMode ? Colors.gray[700] : Colors.gray[900],
148+
borderRadius: BorderRadius.circular(10),
149+
),
150+
child: Stack(
151+
children: [
152+
Column(
153+
crossAxisAlignment: CrossAxisAlignment.start,
154+
children: lines
155+
.map(
156+
(line) => (line is CodeWidgetLine
157+
? line.widget
158+
: line is CodeTextLine
159+
? Text(
160+
line.text,
161+
).mono.small.setColors(lightColor: Colors.white)
162+
: line is CodeCommentLine
163+
? Text(line.text).mono.small.muted
164+
: SizedBox.shrink()),
165+
)
166+
.toList(),
167+
),
168+
Positioned(
169+
right: 0,
170+
child: IconButton(
171+
size: ButtonSize.small,
172+
icon: const Icon(LucideIcons.copy),
173+
variance: ButtonVariance.secondary,
174+
onPressed: () async {
175+
await Clipboard.setData(ClipboardData(text: codeText));
176+
},
177+
),
178+
),
179+
],
180+
),
181+
),
182+
],
183+
),
184+
);
185+
}
186+
}

lib/theme/cubit/theme_state.dart

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
part of 'theme_cubit.dart';
22

33
/// The berlin police blue color scheme.
4-
final berlinPoliceBlue = ColorShades.fromAccent(Color(0xFF005A8C));
4+
final berlinPoliceBlue = ColorShades.fromMap({
5+
50: Colors.transparent,
6+
100: Color.fromRGBO(232, 237, 245, 1),
7+
200: Color.fromRGBO(207, 220, 229, 1),
8+
300: Color.fromRGBO(155, 198, 222, 1),
9+
400: Color.fromRGBO(118, 164, 215, 1),
10+
500: Color.fromRGBO(17, 68, 170, 1),
11+
600: Color.fromRGBO(0, 51, 153, 1),
12+
700: Color.fromRGBO(0, 42, 128, 1),
13+
800: Color.fromRGBO(22, 62, 101, 1),
14+
900: Colors.transparent,
15+
950: Colors.transparent,
16+
});
517

618
/// The radius of the buttons, cards etc.
719
final double radius = 0.5;
820

921
/// The light theme color scheme.
10-
final ColorScheme light = ColorSchemes.lightBlue.copyWith(
22+
final ColorScheme light = ColorSchemes.lightSlate.blue.copyWith(
1123
primary: () => Colors.blue[900],
24+
primaryForeground: () => Colors.white,
1225
// card: () => const Color(0xFFF6F6F6),
1326
// background: () => const Color(0xFFE3E4E7),
1427
// border: () => const Color(0xFFC4C6C6),
@@ -39,7 +52,7 @@ final ThemeData lightTheme = ThemeData(
3952
);
4053

4154
/// The dark theme color scheme.
42-
final ColorScheme dark = ColorSchemes.darkBlue.copyWith(
55+
final ColorScheme dark = ColorSchemes.darkSlate.blue.copyWith(
4356
primary: () => Colors.blue[700],
4457
primaryForeground: () => Colors.white,
4558
card: () => Colors.gray[800],

pubspec.lock

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Generated by pub
22
# See https://dart.dev/tools/pub/glossary#lockfile
33
packages:
4+
animation_kit:
5+
dependency: transitive
6+
description:
7+
name: animation_kit
8+
sha256: d9b0944b3ee02fae3fedbc6cb04d9a9ea26ad1d29f3261e0b55443b1e0bfba63
9+
url: "https://pub.dev"
10+
source: hosted
11+
version: "0.0.2"
412
ansicolor:
513
dependency: transitive
614
description:
@@ -214,6 +222,11 @@ packages:
214222
url: "https://pub.dev"
215223
source: hosted
216224
version: "5.0.0"
225+
flutter_localizations:
226+
dependency: transitive
227+
description: flutter
228+
source: sdk
229+
version: "0.0.0"
217230
flutter_secure_storage:
218231
dependency: "direct main"
219232
description:
@@ -596,10 +609,10 @@ packages:
596609
dependency: "direct main"
597610
description:
598611
name: shadcn_flutter
599-
sha256: "1fd4f798c39d6308dc8f7e94d9e870b5db39fbf417ea95c423c7555ce8227a1c"
612+
sha256: "3a5d92981a0504dd5e42b1f08e413233dbeaa5d075253bb500fe5e71bb01aadc"
600613
url: "https://pub.dev"
601614
source: hosted
602-
version: "0.0.47"
615+
version: "0.0.49"
603616
share_plus:
604617
dependency: transitive
605618
description:

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ dependencies:
5151

5252
# UI Libs
5353
flutter_animate: ^4.5.2
54-
shadcn_flutter: ^0.0.47
54+
shadcn_flutter: ^0.0.49
5555
flutter_spinkit: ^5.2.2
5656
flutter_svg: ^2.2.3
5757

0 commit comments

Comments
 (0)