Skip to content

Commit d940a8b

Browse files
committed
Add the CodeJudge navigation bar
1 parent 96a5954 commit d940a8b

1 file changed

Lines changed: 219 additions & 0 deletions

File tree

lib/code_judge_navigation_bar.dart

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/// Copyright 2026 Fabian Roland (naibaf-1)
2+
/// Licensed under the Apache License, Version 2.0 (the "License");
3+
/// you may not use this file except in compliance with the License.
4+
/// You may obtain a copy of the License at
5+
6+
/// http://www.apache.org/licenses/LICENSE-2.0
7+
8+
/// Unless required by applicable law or agreed to in writing, software
9+
/// distributed under the License is distributed on an "AS IS" BASIS,
10+
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
/// See the License for the specific language governing permissions and
12+
/// limitations under the License.
13+
library;
14+
15+
import 'package:flutter/material.dart';
16+
17+
// Enum handling the layouts
18+
enum CodeJudgeScreenType {mobile, tablet, desktop}
19+
20+
class CodeJudgeNavigationBar extends StatelessWidget {
21+
final Widget body;
22+
final CodeJudgeScreenType screenType;
23+
final String title;
24+
final int selectedIndex;
25+
final ValueChanged<int> onItemSelected;
26+
final List<MyNavigationBarItemData> items;
27+
28+
const CodeJudgeNavigationBar({
29+
super.key,
30+
required this.body,
31+
required this.screenType,
32+
required this.selectedIndex,
33+
required this.onItemSelected,
34+
required this.items,
35+
this.title = 'CodeJudge',
36+
});
37+
38+
@override
39+
Widget build(BuildContext context) {
40+
final theme = Theme.of(context);
41+
42+
if (screenType == CodeJudgeScreenType.desktop) {
43+
return Scaffold(
44+
body: Row(
45+
children: [
46+
Container(
47+
width: 300,
48+
decoration: BoxDecoration(
49+
color: theme.colorScheme.surfaceContainerLow,
50+
),
51+
child: Column(
52+
children: [
53+
Container( // Title
54+
alignment: Alignment.centerLeft,
55+
child: Padding(
56+
padding: const EdgeInsets.only(left: 8.0, top: 20.0, bottom: 20.0),
57+
child: Text(
58+
'CodeJudge',
59+
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold, color: theme.colorScheme.primary)
60+
),
61+
),
62+
),
63+
Divider(thickness: 1, height: 1, color: theme.colorScheme.primary),
64+
for (int i = 0; i < items.length - 1; i++) // Items
65+
MyNavigationBarItem(
66+
icon: items[i].icon,
67+
label: items[i].label,
68+
selected: selectedIndex == i,
69+
onTap: () => onItemSelected(i),
70+
),
71+
const Spacer(),
72+
Divider(thickness: 1, height: 1, color: theme.colorScheme.primary),
73+
MyNavigationBarItem( // Settings
74+
icon: items.last.icon,
75+
label: items.last.label,
76+
selected: selectedIndex == items.length - 1,
77+
onTap: () => onItemSelected(items.length - 1),
78+
),
79+
],
80+
),
81+
),
82+
VerticalDivider(thickness: 1, width: 1, color: theme.colorScheme.primary),
83+
Expanded(child: body),
84+
],
85+
),
86+
);
87+
} else if (screenType == CodeJudgeScreenType.tablet) {
88+
return Scaffold(
89+
body: Row(
90+
children: [
91+
NavigationRail(
92+
backgroundColor: theme.colorScheme.surfaceContainerLow,
93+
selectedIndex: selectedIndex,
94+
labelType: NavigationRailLabelType.all,
95+
selectedIconTheme: IconThemeData(color: theme.colorScheme.primary, size: 30),
96+
unselectedIconTheme: IconThemeData(color: theme.colorScheme.outline, size: 25),
97+
selectedLabelTextStyle: TextStyle(color: theme.colorScheme.primary, fontWeight: FontWeight.bold),
98+
unselectedLabelTextStyle: TextStyle(color: theme.colorScheme.outline),
99+
onDestinationSelected: onItemSelected,
100+
destinations: [ // Items
101+
for (final item in items)
102+
NavigationRailDestination(icon: Icon(item.icon), label: Text(item.label)),
103+
],
104+
),
105+
VerticalDivider(thickness: 1, width: 1, color: theme.colorScheme.primary),
106+
Expanded(child: body),
107+
],
108+
),
109+
);
110+
} else {
111+
return Scaffold(
112+
appBar: AppBar(title: Text(title), backgroundColor: theme.colorScheme.primaryContainer),
113+
drawer: Drawer(
114+
backgroundColor: theme.colorScheme.surfaceContainerLow,
115+
child: Column(
116+
children: [
117+
DrawerHeader(
118+
child: Container(
119+
alignment: Alignment.centerLeft,
120+
child: Text(
121+
'CodeJudge',
122+
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold, color: theme.colorScheme.primary)
123+
),
124+
),
125+
),
126+
Expanded(
127+
child: Column(
128+
children: [
129+
for (int i = 0; i < items.length - 1; i++) // Items
130+
MyNavigationBarItem(
131+
icon: items[i].icon,
132+
label: items[i].label,
133+
selected: selectedIndex == i,
134+
onTap: () => onItemSelected(i),
135+
),
136+
const Spacer(),
137+
Divider(thickness: 1, height: 1),
138+
MyNavigationBarItem( // Settings
139+
icon: items.last.icon,
140+
label: items.last.label,
141+
selected: selectedIndex == items.length - 1,
142+
onTap: () => onItemSelected(items.length - 1),
143+
),
144+
],
145+
),
146+
),
147+
],
148+
),
149+
),
150+
body: body,
151+
);
152+
}
153+
}
154+
}
155+
156+
class MyNavigationBarItemData {
157+
final IconData icon;
158+
final String label;
159+
160+
const MyNavigationBarItemData({
161+
required this.icon,
162+
required this.label,
163+
});
164+
}
165+
166+
class MyNavigationBarItem extends StatelessWidget {
167+
final IconData icon;
168+
final String label;
169+
final bool selected;
170+
final VoidCallback onTap;
171+
172+
const MyNavigationBarItem({
173+
super.key,
174+
required this.icon,
175+
required this.label,
176+
required this.selected,
177+
required this.onTap,
178+
});
179+
180+
@override
181+
Widget build(BuildContext context) {
182+
final theme = Theme.of(context);
183+
184+
return Padding(
185+
padding: const EdgeInsets.only(top: 4),
186+
child: Material(
187+
borderRadius: BorderRadius.circular(8.0),
188+
color: selected ? theme.colorScheme.primary.withAlpha(30) : theme.colorScheme.surfaceContainerLow,
189+
child: InkWell(
190+
onTap: onTap,
191+
borderRadius: BorderRadius.circular(8.0),
192+
splashColor: selected ? Colors.transparent : theme.colorScheme.primary.withAlpha(32),
193+
hoverColor: selected ? Colors.transparent : theme.colorScheme.tertiary.withAlpha(32),
194+
child: Padding(
195+
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
196+
child: Row(
197+
children: [
198+
Icon(
199+
icon,
200+
size: 24,
201+
color: selected ? theme.colorScheme.primary : theme.colorScheme.onSurface.withAlpha(244),
202+
),
203+
const SizedBox(width: 8.0),
204+
Text(
205+
label,
206+
style: TextStyle(
207+
fontSize: 15,
208+
fontWeight: selected ? FontWeight.bold : FontWeight.w300,
209+
color: selected ? theme.colorScheme.primary : theme.colorScheme.onSurface.withAlpha(244),
210+
),
211+
),
212+
],
213+
),
214+
),
215+
),
216+
),
217+
);
218+
}
219+
}

0 commit comments

Comments
 (0)