feat: initial Pyramid project scaffold

This commit is contained in:
Bernd Steckmeister
2026-04-19 19:18:12 +02:00
commit 062b8b6a94
138 changed files with 6001 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pyramid/core/router.dart';
import 'package:pyramid/core/theme.dart';
class PyramidApp extends ConsumerWidget {
const PyramidApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider);
final colorScheme = ref.watch(colorSchemeProvider);
return MaterialApp.router(
title: 'Pyramid',
theme: buildTheme(colorScheme, Brightness.light),
darkTheme: buildTheme(colorScheme, Brightness.dark),
themeMode: ThemeMode.system,
routerConfig: router,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('de'), Locale('en')],
);
}
}
+32
View File
@@ -0,0 +1,32 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:pyramid/layout/shell_page.dart';
import 'package:pyramid/features/rooms/rooms_page.dart';
import 'package:pyramid/features/chat/chat_page.dart';
import 'package:pyramid/features/settings/settings_page.dart';
final routerProvider = Provider<GoRouter>((ref) {
return GoRouter(
initialLocation: '/rooms',
routes: [
ShellRoute(
builder: (context, state, child) => ShellPage(child: child),
routes: [
GoRoute(
path: '/rooms',
builder: (context, state) => const RoomsPage(),
),
GoRoute(
path: '/chat/:roomId',
builder: (context, state) =>
ChatPage(roomId: state.pathParameters['roomId']!),
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsPage(),
),
],
),
],
);
});
+24
View File
@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final colorSchemeProvider = Provider<ColorScheme>((ref) {
return ColorScheme.fromSeed(
seedColor: const Color(0xFF6B4FA0),
brightness: Brightness.light,
);
});
ThemeData buildTheme(ColorScheme colorScheme, Brightness brightness) {
final scheme = brightness == Brightness.dark
? ColorScheme.fromSeed(
seedColor: const Color(0xFF6B4FA0),
brightness: Brightness.dark,
)
: colorScheme;
return ThemeData(
colorScheme: scheme,
useMaterial3: true,
dividerTheme: const DividerThemeData(space: 1, thickness: 1),
);
}
+14
View File
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class ChatPage extends StatelessWidget {
final String roomId;
const ChatPage({super.key, required this.roomId});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(roomId)),
body: const Center(child: Text('Chat')),
);
}
}
+12
View File
@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
class RoomsPage extends StatelessWidget {
const RoomsPage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: Text('Rooms')),
);
}
}
+12
View File
@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
class SettingsPage extends StatelessWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: Text('Settings')),
);
}
}
+52
View File
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pyramid/features/rooms/rooms_page.dart';
final sidebarWidthProvider = StateProvider<double>((ref) => 280);
class PanelLayout extends ConsumerWidget {
final Widget mainContent;
const PanelLayout({super.key, required this.mainContent});
@override
Widget build(BuildContext context, WidgetRef ref) {
final sidebarWidth = ref.watch(sidebarWidthProvider);
return Row(
children: [
SizedBox(
width: sidebarWidth,
child: const RoomsPage(),
),
_ResizeDivider(
onDrag: (delta) {
final notifier = ref.read(sidebarWidthProvider.notifier);
notifier.state =
(notifier.state + delta).clamp(180.0, 480.0);
},
),
Expanded(child: mainContent),
],
);
}
}
class _ResizeDivider extends StatelessWidget {
final ValueChanged<double> onDrag;
const _ResizeDivider({required this.onDrag});
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onHorizontalDragUpdate: (d) => onDrag(d.delta.dx),
child: MouseRegion(
cursor: SystemMouseCursors.resizeColumn,
child: Container(
width: 6,
color: Theme.of(context).dividerColor,
),
),
);
}
}
+19
View File
@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pyramid/layout/panel_layout.dart';
class ShellPage extends ConsumerWidget {
final Widget child;
const ShellPage({super.key, required this.child});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isWide = MediaQuery.sizeOf(context).width >= 700;
return Scaffold(
body: isWide
? PanelLayout(mainContent: child)
: child,
);
}
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pyramid/core/app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const ProviderScope(child: PyramidApp()));
}