complete project

This commit is contained in:
baldeau 2024-03-09 14:29:48 +01:00
commit 965a9439c2
173 changed files with 11200 additions and 0 deletions

51
.gitignore vendored Normal file
View File

@ -0,0 +1,51 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# Rust related
.cargo/
target/
# Generated messages
*/**/messages/

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "packages/real_time_chart"]
path = packages/real_time_chart
url = https://avoid.sh/bachelor/real_time_chart.git
[submodule "packages/virtual_keyboard_multi_language"]
path = packages/virtual_keyboard_multi_language
url = https://avoid.sh/bachelor/virtual_keyboard_multi_language.git

33
.metadata Normal file
View File

@ -0,0 +1,33 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "7482962148e8d758338d8a28f589f317e1e42ba4"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: linux
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: macos
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

3
.puro.json Normal file
View File

@ -0,0 +1,3 @@
{
"env": "bachelor_elinux"
}

19
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"macFrameworkPath": [
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "macos-clang-arm64"
}
],
"version": 4
}

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"dart.flutterSdkPath": "/Users/fabian/.puro/envs/bachelor_elinux/flutter",
"dart.sdkPath": "/Users/fabian/.puro/envs/bachelor_elinux/flutter/bin/cache/dart-sdk"
}

16
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "make",
"type": "shell",
"command": "dart run build_runner build --delete-conflicting-outputs",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

29
LICENSE Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2023, Fabian Baldeau
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
release:
flutter pub global run flutterpi_tool build --arch=arm64 --cpu=pi4 --release
profile:
flutter pub global run flutterpi_tool build --arch=arm64 --cpu=pi4 --profile
sync-release:
rsync -a ./build/flutter_assets/ fabian@raspberrypi:/home/fabian/release
sync-profile:
rsync -a ./build/flutter_assets/ fabian@raspberrypi:/home/fabian/profile
stop:
ssh -o BatchMode=no fabian@raspberrypi pkill flutter-pi
dep-graph:
layerlens

16
README.md Normal file
View File

@ -0,0 +1,16 @@
# flutter_elinux
A demo Flutter project for embedded Linux systems.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

32
analysis_options.yaml Normal file
View File

@ -0,0 +1,32 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
analyzer:
plugins:
- custom_lint

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
assets/img/raspi_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/video/video_720p.mp4 Normal file

Binary file not shown.

36
lib/app.dart Normal file
View File

@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'common/settings/app_settings.dart';
import 'constants/colors.dart';
import 'common/routing/router.dart';
class App extends ConsumerWidget {
const App({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
bool useMaterial3 = ref.watch(useMaterial3Provider);
bool useDarkMode = ref.watch(useDarkModeProvider);
bool showPerformanceOverlay = ref.watch(showPerformanceOverlayProvider);
ColorSeed colorSeedSelected = ref.watch(colorSeedSelectedProvider);
return MaterialApp.router(
showPerformanceOverlay: showPerformanceOverlay,
routerConfig: goRouter,
debugShowCheckedModeBanner: false,
title: 'eLinux',
theme: ThemeData(
colorSchemeSeed: colorSeedSelected.color,
useMaterial3: useMaterial3,
brightness: Brightness.light,
),
darkTheme: ThemeData(
colorSchemeSeed: colorSeedSelected.color,
useMaterial3: useMaterial3,
brightness: Brightness.dark,
),
themeMode: useDarkMode ? ThemeMode.dark : ThemeMode.light,
);
}
}

View File

@ -0,0 +1,3 @@
extension StringExtension on String {
Iterable<int> getCharCodes() => codeUnits;
}

View File

@ -0,0 +1,11 @@
import 'package:intl/intl.dart';
extension StringExtension on String {
String toFormattedNewsDate() {
final parsedDate = DateTime.parse(this);
// The intl package is used here for more robust formatting options
final DateFormat formatter = DateFormat('dd.MM.yyyy HH:mm', 'de_DE');
return "Stand: ${formatter.format(parsedDate)} Uhr";
}
}

View File

@ -0,0 +1,203 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:nm/nm.dart';
import '../../features/benchmark/presentation/benchmark_screen.dart';
import '../../features/benchmark/presentation/map_benchmark_screen.dart';
import '../../features/benchmark/presentation/rogue_shooter_screen.dart';
import '../../features/benchmark/presentation/sprite_benchmark_screen.dart';
import '../../features/benchmark/presentation/video_benchmark_screen.dart';
import '../../features/benchmark/presentation/vsync_benchmark_screen.dart';
import '../../features/home/presentation/home_screen.dart';
import '../../features/material_demo/presentation/material_demo_screen.dart';
import '../../features/matrix_rgb/presentation/matrix_screen.dart';
import '../../features/morse_led/presentation/led_screen.dart';
import '../../features/news_api/presentation/news_screen.dart';
import '../../features/settings/presentation/settings_screen/settings_screen.dart';
import '../../features/settings/presentation/wifi_settings_screen/accesspoint_screen.dart';
import '../../features/settings/presentation/wifi_settings_screen/wifi_settings_screen.dart';
import '../../features/system_resources/presentation/system_resources_screen.dart';
import '../widgets/home_scaffold.dart';
import 'routes.dart';
const ValueKey<String> _scaffoldKey = ValueKey<String>('App scaffold');
final goRouter = GoRouter(
initialLocation: '/',
debugLogDiagnostics: false,
routes: [
GoRoute(
path: '/',
redirect: (_, __) => '/home',
),
GoRoute(
path: '/home',
name: RoutesHome.home.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.home,
child: HomeScreen(),
),
),
),
GoRoute(
path: '/material-demo',
name: RoutesHome.materialDemo.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.materialDemo,
child: MaterialDemoScreen(),
),
),
),
GoRoute(
path: '/news',
name: RoutesHome.news.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.news,
child: NewsScreen(),
),
),
),
GoRoute(
path: '/matrix',
name: RoutesHome.matrix.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.matrix,
child: MatrixScreen(),
),
),
),
GoRoute(
path: '/led',
name: RoutesHome.led.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.led,
child: LedScreen(),
),
),
),
GoRoute(
path: '/system',
name: RoutesHome.system.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.system,
child: SystemResourcesScreen(),
),
),
),
GoRoute(
path: '/benchmark',
name: RoutesHome.benchmark.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.benchmark,
child: BenchmarkScreen(),
),
),
routes: [
GoRoute(
path: RoutesBenchmark.sprites.name,
name: RoutesBenchmark.sprites.name,
builder: (context, state) => const SpriteBenchmarkScreen(),
),
GoRoute(
path: RoutesBenchmark.rogueShooter.name,
name: RoutesBenchmark.rogueShooter.name,
builder: (context, state) => const RogueShooterScreen(),
),
GoRoute(
path: RoutesBenchmark.shader.name,
name: RoutesBenchmark.shader.name,
builder: (context, state) => const VsyncBenchmarkScreen(),
),
GoRoute(
path: RoutesBenchmark.video.name,
name: RoutesBenchmark.video.name,
builder: (context, state) => const VideoBenchmarkScreen(),
),
GoRoute(
path: RoutesBenchmark.map.name,
name: RoutesBenchmark.map.name,
builder: (context, state) => const MapBenchmarkScreen(),
),
],
),
GoRoute(
path: '/settings',
name: RoutesHome.settings.name,
pageBuilder: (BuildContext context, GoRouterState state) =>
FadeTransitionPage(
key: _scaffoldKey,
child: const HomeScaffold(
selectedTab: RoutesHome.settings,
child: SettingsScreen(),
),
),
routes: [
GoRoute(
path: RoutesSettings.wifi.name,
name: RoutesSettings.wifi.name,
builder: (context, state) => const WifiSettingsScreen(),
routes: [
GoRoute(
path: 'accesspoint',
name: RoutesSettings.accesspoint.name,
builder: (context, state) {
if (state.extra == null) {
return const AccessPointScreen();
}
final accessPoint = state.extra as NetworkManagerAccessPoint;
return AccessPointScreen(
accessPoint: accessPoint,
);
},
),
],
),
],
),
],
);
/// A page that fades in and out.
/// From the official Go Router Example:
/// https://github.com/flutter/packages/blob/6701c9e618041574cf4cc53cb028a38de8322b53/packages/go_router/example/lib/books/main.dart#L154
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
class FadeTransitionPage extends CustomTransitionPage<void> {
/// Creates a [FadeTransitionPage].
FadeTransitionPage({
required LocalKey super.key,
required super.child,
}) : super(
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) =>
FadeTransition(
opacity: animation.drive(_curveTween),
child: child,
));
static final CurveTween _curveTween = CurveTween(curve: Curves.easeIn);
}

View File

@ -0,0 +1,29 @@
enum RoutesHome {
home,
materialDemo,
benchmark,
news,
led,
matrix,
system,
settings,
}
enum RoutesSettings {
wifi,
accesspoint,
}
enum RoutesBenchmark {
sprites,
rogueShooter,
shader,
video,
map,
}
enum RoutesEndlessRunner {
play,
session,
settings,
}

View File

@ -0,0 +1,61 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../constants/colors.dart';
import 'sharedPrefs.dart';
part 'app_settings.g.dart';
@riverpod
class UseMaterial3 extends _$UseMaterial3 {
@override
bool build() {
final bool material3 = SharedPrefs().getValue('material3') ?? true;
return material3;
}
void toggle() {
SharedPrefs().setValue('material3', !state);
state = !state;
}
}
@riverpod
class UseDarkMode extends _$UseDarkMode {
@override
bool build() {
final bool darkMode = SharedPrefs().getValue('darkMode') ?? false;
return darkMode;
}
void toggle() {
SharedPrefs().setValue('darkMode', !state);
state = !state;
}
}
@riverpod
class ColorSeedSelected extends _$ColorSeedSelected {
@override
ColorSeed build() {
final String colorSeed =
SharedPrefs().getValue('colorSeed') ?? ColorSeed.baseColor.label;
return ColorSeed.values.firstWhere((element) => element.label == colorSeed);
}
void setColorSeed(ColorSeed colorSeed) {
SharedPrefs().setValue('colorSeed', colorSeed.label);
state = colorSeed;
}
}
@riverpod
class ShowPerformanceOverlay extends _$ShowPerformanceOverlay {
@override
bool build() {
return false;
}
void toggle() {
state = !state;
}
}

View File

@ -0,0 +1,73 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'app_settings.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$useMaterial3Hash() => r'38dbfd0b19379fa8ee73c1b0ec03dfe914dc23bc';
/// See also [UseMaterial3].
@ProviderFor(UseMaterial3)
final useMaterial3Provider =
AutoDisposeNotifierProvider<UseMaterial3, bool>.internal(
UseMaterial3.new,
name: r'useMaterial3Provider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$useMaterial3Hash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$UseMaterial3 = AutoDisposeNotifier<bool>;
String _$useDarkModeHash() => r'd9f1ffc5d34dc2c9a59b99e0393b8f5ccc0a7040';
/// See also [UseDarkMode].
@ProviderFor(UseDarkMode)
final useDarkModeProvider =
AutoDisposeNotifierProvider<UseDarkMode, bool>.internal(
UseDarkMode.new,
name: r'useDarkModeProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$useDarkModeHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$UseDarkMode = AutoDisposeNotifier<bool>;
String _$colorSeedSelectedHash() => r'13745dbe8aee72eb6d43f49cb0d155164a96eb8c';
/// See also [ColorSeedSelected].
@ProviderFor(ColorSeedSelected)
final colorSeedSelectedProvider =
AutoDisposeNotifierProvider<ColorSeedSelected, ColorSeed>.internal(
ColorSeedSelected.new,
name: r'colorSeedSelectedProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$colorSeedSelectedHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ColorSeedSelected = AutoDisposeNotifier<ColorSeed>;
String _$showPerformanceOverlayHash() =>
r'8fb9b73ed0c32730b2b83cf0e6db78f066603ac4';
/// See also [ShowPerformanceOverlay].
@ProviderFor(ShowPerformanceOverlay)
final showPerformanceOverlayProvider =
AutoDisposeNotifierProvider<ShowPerformanceOverlay, bool>.internal(
ShowPerformanceOverlay.new,
name: r'showPerformanceOverlayProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$showPerformanceOverlayHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ShowPerformanceOverlay = AutoDisposeNotifier<bool>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -0,0 +1,88 @@
import 'package:virtual_keyboard_multi_language/virtual_keyboard_multi_language.dart';
// example custom layout file (example from the virtual_keyboard_multi_language package)
// here different layouts for different languages could be defined:
class CustomLayoutKeys extends VirtualKeyboardLayoutKeys {
@override
int getLanguagesCount() => 2;
@override
List<List> getLanguage(int index) {
switch (index) {
case 1:
return _arabicLayout;
default:
return defaultEnglishLayout;
}
}
}
const List<List> _arabicLayout = [
// Row 1
[
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'0',
],
// Row 2
[
'ض',
'ص',
'ث',
'ق',
'ف',
'غ',
'ع',
'ه',
'خ',
'ح',
'د',
VirtualKeyboardKeyAction.Backspace
],
// Row 3
[
'ش',
'س',
'ي',
'ب',
'ل',
'ا',
'ت',
'ن',
'م',
'ك',
'ط',
VirtualKeyboardKeyAction.Return
],
// Row 4
[
'ذ',
'ئ',
'ء',
'ؤ',
'ر',
'لا',
'ى',
'ة',
'و',
'.',
'ظ',
VirtualKeyboardKeyAction.Shift
],
// Row 5
[
VirtualKeyboardKeyAction.SwithLanguage,
'@',
VirtualKeyboardKeyAction.Space,
'-',
'_',
]
];

View File

@ -0,0 +1,31 @@
import 'package:shared_preferences/shared_preferences.dart';
/// A class to handle shared preferences
class SharedPrefs {
late final SharedPreferences _sharedPrefs;
static final SharedPrefs _instance = SharedPrefs._internal();
factory SharedPrefs() => _instance;
SharedPrefs._internal();
Future<void> init() async {
_sharedPrefs = await SharedPreferences.getInstance();
}
setValue(String key, dynamic value) {
if (value is bool) {
_sharedPrefs.setBool(key, value);
} else if (value is int) {
_sharedPrefs.setInt(key, value);
} else if (value is double) {
_sharedPrefs.setDouble(key, value);
} else if (value is String) {
_sharedPrefs.setString(key, value);
} else if (value is List<String>) {
_sharedPrefs.setStringList(key, value);
}
}
dynamic getValue(String key) {
return _sharedPrefs.get(key);
}
}

View File

@ -0,0 +1,13 @@
import 'dart:io';
Future<void> linuxShutdown({bool restart = false, int delay = 0}) {
final params = <String>[];
if (restart) {
params.add('-r');
}
params.addAll(<String>['-t', delay.toString(), 'now']);
return Process.run('sudo', ['shutdown', ...params]);
}

View File

@ -0,0 +1,6 @@
Stream<DateTime> timeStream() async* {
while (true) {
await Future.delayed(const Duration(milliseconds: 100));
yield DateTime.now();
}
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import '../utils/time_stream.dart';
class ClockWidget extends ConsumerWidget {
const ClockWidget({this.textStyle, super.key});
final TextStyle? textStyle;
@override
Widget build(BuildContext context, WidgetRef ref) {
return StreamBuilder<DateTime>(
stream: timeStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
DateTime time = snapshot.data ?? DateTime.now();
DateFormat format = DateFormat('HH:mm:ss');
return Text(
format.format(time),
style: textStyle,
);
} else {
return const CircularProgressIndicator();
}
},
);
}
}

View File

@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class FloatingMenuButton extends StatelessWidget {
const FloatingMenuButton({this.popupMenuButton, super.key});
final PopupMenuButton? popupMenuButton;
@override
Widget build(BuildContext context) {
return PositionedDirectional(
start: 16,
top: 16,
child: SafeArea(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(999),
boxShadow: [
BoxShadow(
// check if dark mode
color: (Theme.of(context).brightness == Brightness.light)
? Colors.grey.withOpacity(0.5)
: Colors.black.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
padding: const EdgeInsets.all(8),
child: Row(
children: [
IconButton(
onPressed: () => context.pop(),
icon: const Icon(Icons.arrow_back),
),
if (popupMenuButton != null)
Row(
children: [
const SizedBox(width: 8),
popupMenuButton!,
const SizedBox(width: 8),
],
)
],
),
),
),
);
}
}

View File

@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
import 'package:go_router/go_router.dart';
import '../routing/routes.dart';
import 'scaffold_with_statusbar.dart';
/// The Scaffold that is used for the home screen.
class HomeScaffold extends StatelessWidget {
const HomeScaffold({
required this.selectedTab,
required this.child,
this.appBar,
super.key,
});
/// Which tab of the scaffold to display.
final RoutesHome selectedTab;
/// The scaffold body.
final Widget child;
/// The scaffold AppBar.
final AppBar? appBar;
@override
Widget build(BuildContext context) => ScaffoldWithStatusbar(
body: AdaptiveScaffold(
appBar: appBar,
smallBreakpoint: const WidthPlatformBreakpoint(end: 700),
mediumBreakpoint:
const WidthPlatformBreakpoint(begin: 700, end: 1400),
largeBreakpoint: const WidthPlatformBreakpoint(begin: 1400),
body: (_) => child,
selectedIndex: selectedTab.index,
destinations: const <NavigationDestination>[
NavigationDestination(
label: 'Home',
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
),
NavigationDestination(
label: 'Material Demo',
icon: Icon(Icons.widgets_outlined),
selectedIcon: Icon(Icons.widgets),
),
NavigationDestination(
label: 'Benchmark',
icon: Icon(Icons.fireplace_outlined),
selectedIcon: Icon(Icons.fireplace),
),
NavigationDestination(
label: 'News',
icon: Icon(Icons.newspaper_outlined),
selectedIcon: Icon(Icons.newspaper),
),
NavigationDestination(
label: 'Led',
icon: Icon(Icons.fluorescent_outlined),
selectedIcon: Icon(Icons.fluorescent),
),
NavigationDestination(
label: 'Matrix',
icon: Icon(Icons.grid_off),
selectedIcon: Icon(Icons.grid_on),
),
NavigationDestination(
label: 'System',
icon: Icon(Icons.insert_chart_outlined_outlined),
selectedIcon: Icon(Icons.insert_chart),
),
NavigationDestination(
label: 'Settings',
icon: Icon(Icons.settings_outlined),
selectedIcon: Icon(Icons.settings),
),
],
onSelectedIndexChange: (int idx) {
switch (RoutesHome.values[idx]) {
case RoutesHome.home:
context.goNamed(RoutesHome.home.name);
case RoutesHome.materialDemo:
context.goNamed(RoutesHome.materialDemo.name);
case RoutesHome.news:
context.goNamed(RoutesHome.news.name);
case RoutesHome.benchmark:
context.goNamed(RoutesHome.benchmark.name);
case RoutesHome.led:
context.goNamed(RoutesHome.led.name);
case RoutesHome.matrix:
context.goNamed(RoutesHome.matrix.name);
case RoutesHome.system:
context.goNamed(RoutesHome.system.name);
case RoutesHome.settings:
context.goNamed(RoutesHome.settings.name);
}
},
),
);
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../routing/routes.dart';
class MenuDrawer extends StatelessWidget {
const MenuDrawer({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: <Widget>[
const DrawerHeader(
child: Column(
children: [],
)),
ListTile(
selected: GoRouterState.of(context).uri.toString() ==
GoRouter.of(context).namedLocation(RoutesHome.home.name),
title: const Text('Home'),
leading: const Icon(Icons.home),
onTap: () {
context.replaceNamed(RoutesHome.home.name);
},
),
ListTile(
selected: GoRouterState.of(context).uri.toString() ==
GoRouter.of(context).namedLocation(RoutesHome.news.name),
title: const Text('News'),
leading: const Icon(Icons.newspaper),
onTap: () {
context.replaceNamed(RoutesHome.news.name);
},
),
ListTile(
selected: GoRouterState.of(context).uri.toString() ==
GoRouter.of(context).namedLocation(RoutesHome.news.name),
title: const Text('Led'),
leading: const Icon(Icons.newspaper),
onTap: () {
context.replaceNamed(RoutesHome.led.name);
},
),
ListTile(
selected: GoRouterState.of(context).uri.toString() ==
GoRouter.of(context).namedLocation(RoutesHome.news.name),
title: const Text('Matrix'),
leading: const Icon(Icons.newspaper),
onTap: () {
context.replaceNamed(RoutesHome.matrix.name);
},
),
ListTile(
selected: GoRouterState.of(context).uri.toString() ==
GoRouter.of(context).namedLocation(RoutesHome.settings.name),
title: const Text('Settings'),
leading: const Icon(Icons.settings),
onTap: () {
context.replaceNamed(RoutesHome.settings.name);
},
),
],
),
);
}
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'statusbar.dart';
class ScaffoldWithStatusbar extends StatelessWidget {
const ScaffoldWithStatusbar({
this.appBar,
this.body,
this.floatingActionButton,
super.key,
});
final AppBar? appBar;
final Widget? body;
final FloatingActionButton? floatingActionButton;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
const StatusBar(),
Expanded(
child: Scaffold(
appBar: appBar,
body: body,
floatingActionButton: floatingActionButton,
),
),
],
),
);
}
}

View File

@ -0,0 +1,113 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:nm/nm.dart';
import '../../features/settings/business/wifi_controller.dart';
import '../../features/settings/presentation/wifi_settings_screen/widgets/wifi_bar_icon.dart';
import 'clock.dart';
const horizontalSpace = SizedBox(
width: 8,
);
class StatusBar extends ConsumerWidget {
const StatusBar({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final wifiStream = (Platform.isLinux)
? ref.watch(wifiControllerProvider.notifier).wifiStream()
: const Stream.empty();
return Container(
width: double.infinity,
height: 24,
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Row(
children: [
horizontalSpace,
kDebugMode
? Text(
'DEBUG MODE',
style: TextStyle(color: Colors.white),
)
: SizedBox(),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
StreamBuilder(
stream: wifiStream,
builder: (context, snapshot) {
if (!Platform.isLinux) {
return const Row(
children: [
Text(
'DemoConnection',
style: TextStyle(color: Colors.white),
),
horizontalSpace,
WifiBarIcon(
strength: 1,
size: 16,
color: Colors.white,
),
],
);
}
if (snapshot.hasData) {
final data = snapshot.data?.$1 as NetworkManagerDevice;
return Row(
children: [
Text(
data.activeConnection?.id ?? 'No connection',
style: const TextStyle(color: Colors.white),
),
horizontalSpace,
WifiBarIcon(
strength:
data.wireless?.activeAccessPoint?.strength ?? 0,
size: 16,
color: Colors.white,
),
],
);
}
if (snapshot.hasError) {
// error can be caught here
// display error in statusbar:
// return Text(
// snapshot.error.toString(),
// style: const TextStyle(color: Colors.white),
// );
// show nothing (hide error message from user):
// the error that can happen here is mostly related
// to not finding any usable wifi device
return const SizedBox();
}
return const Text(
"No connection",
style: TextStyle(color: Colors.white),
);
},
),
horizontalSpace,
const ClockWidget(
textStyle: TextStyle(color: Colors.white),
),
horizontalSpace,
],
),
],
),
);
}
}

View File

@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:virtual_keyboard_multi_language/virtual_keyboard_multi_language.dart';
import '../settings/custom_layout.dart';
class TextInputDialog extends ConsumerWidget {
const TextInputDialog({
required this.controller,
this.title = 'Text Input',
this.onReturnPress,
this.hintText,
super.key,
});
final TextEditingController controller;
final String title;
final Function? onReturnPress;
final String? hintText;
@override
Widget build(BuildContext context, WidgetRef ref) {
return TextField(
controller: controller,
decoration: InputDecoration(hintText: hintText),
onTap: () {
// always move the cursor to the end position by default
controller.value = TextEditingValue(
text: controller.text,
selection: TextSelection.collapsed(
offset: controller.text.length,
),
);
showDialog(
context: context,
builder: (context) => Dialog.fullscreen(
// backgroundColor: Colors.transparent,
child: ClipRect(
// TODO: Blur background causes lag on the raspberry pi
// BackdropFilter() with:
// filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Scaffold(
backgroundColor:
Theme.of(context).colorScheme.background.withOpacity(0.5),
appBar: AppBar(
// backgroundColor: Colors.transparent,
title: Text(title),
centerTitle: false,
leading: IconButton(
onPressed: () {
FocusScope.of(context).unfocus();
context.pop();
},
icon: const Icon(Icons.close),
),
),
body: Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.topCenter,
child: Container(
constraints: const BoxConstraints(maxWidth: 400),
child: TextField(
controller: controller,
autofocus: true,
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: TextFieldTapRegion(
child: VirtualKeyboard(
textController: controller,
textColor:
Theme.of(context).textTheme.headlineLarge!.color!,
type: VirtualKeyboardType.Alphanumeric,
// onKeyPress: (key) => onKeyPress(key, context),
customLayoutKeys: CustomLayoutKeys(),
onReturnKey: onReturnPress,
unfocus: true,
),
),
),
],
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,8 @@
class TagesschauApiUrls {
static const main = 'https://www.tagesschau.de/api2/';
static const news = '$main/news/';
static const placeholderImage =
'https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Placeholder_view_vector.svg/1362px-Placeholder_view_vector.svg.png';
static const logoImage =
'https://images.tagesschau.de/image/89045d82-5cd5-46ad-8f91-73911add30ee/AAABh3YLLz0/AAABjcWen7M/16x9-1280/tagesschau-logo-100.jpg';
}

17
lib/constants/colors.dart Normal file
View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
enum ColorSeed {
baseColor('M3 Baseline', Color(0xff6750a4)),
indigo('Indigo', Colors.indigo),
blue('Blue', Colors.blue),
teal('Teal', Colors.teal),
green('Green', Colors.green),
yellow('Yellow', Colors.yellow),
orange('Orange', Colors.orange),
deepOrange('Deep Orange', Colors.deepOrange),
pink('Pink', Colors.pink);
const ColorSeed(this.label, this.color);
final String label;
final Color color;
}

View File

@ -0,0 +1,3 @@
# Flame Business Layer
Custom Layer for the benchmarks that use the Flame engine.

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Blue Fire
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,10 @@
# Flame Performance Test Game
This is a simple scrolling shooter game which we use for testing the performance of Flame,
since it uses a lot of components and hitboxes. When it reaches a certain amount of
components (counted in the lower right corner) you can expect it to drop a bit in FPS,
depending on what platform you are on.
# Source
https://github.com/flame-engine/flame/tree/main/examples/games/rogue_shooter

View File

@ -0,0 +1,55 @@
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'enemy_component.dart';
class BulletComponent extends SpriteAnimationComponent
with HasGameRef, CollisionCallbacks {
static const speed = 500.0;
late final Vector2 velocity;
final Vector2 deltaPosition = Vector2.zero();
BulletComponent({required super.position, super.angle})
: super(size: Vector2(10, 20), anchor: Anchor.center);
@override
Future<void> onLoad() async {
add(CircleHitbox());
animation = await game.loadSpriteAnimation(
'flame_benchmark/rogue_shooter/bullet.png',
SpriteAnimationData.sequenced(
stepTime: 0.2,
amount: 4,
textureSize: Vector2(8, 16),
),
);
velocity = Vector2(0, -1)
..rotate(angle)
..scale(speed);
}
@override
void onCollisionStart(
Set<Vector2> intersectionPoints,
PositionComponent other,
) {
super.onCollisionStart(intersectionPoints, other);
if (other is EnemyComponent) {
other.takeHit();
removeFromParent();
}
}
@override
void update(double dt) {
super.update(dt);
deltaPosition
..setFrom(velocity)
..scale(dt);
position += deltaPosition;
if (position.y < 0 || position.x > game.size.x || position.x + size.x < 0) {
removeFromParent();
}
}
}

View File

@ -0,0 +1,43 @@
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import '../rogue_shooter_game.dart';
import 'explosion_component.dart';
class EnemyComponent extends SpriteAnimationComponent
with HasGameReference<RogueShooterGame>, CollisionCallbacks {
static const speed = 150;
static final Vector2 initialSize = Vector2.all(25);
EnemyComponent({required super.position})
: super(size: initialSize, anchor: Anchor.center);
@override
Future<void> onLoad() async {
animation = await game.loadSpriteAnimation(
'flame_benchmark/rogue_shooter/enemy.png',
SpriteAnimationData.sequenced(
stepTime: 0.2,
amount: 4,
textureSize: Vector2.all(16),
),
);
add(CircleHitbox(collisionType: CollisionType.passive));
}
@override
void update(double dt) {
super.update(dt);
y += speed * dt;
if (y >= game.size.y) {
removeFromParent();
}
}
void takeHit() {
removeFromParent();
game.add(ExplosionComponent(position: position));
game.increaseScore();
}
}

View File

@ -0,0 +1,27 @@
import 'dart:math';
import 'package:flame/components.dart';
import 'enemy_component.dart';
class EnemyCreator extends TimerComponent with HasGameRef {
final Random random = Random();
final _halfWidth = EnemyComponent.initialSize.x / 2;
EnemyCreator() : super(period: 0.05, repeat: true);
@override
void onTick() {
game.addAll(
List.generate(
5,
(index) => EnemyComponent(
position: Vector2(
_halfWidth + (game.size.x - _halfWidth) * random.nextDouble(),
0,
),
),
),
);
}
}

View File

@ -0,0 +1,23 @@
import 'package:flame/components.dart';
class ExplosionComponent extends SpriteAnimationComponent with HasGameRef {
ExplosionComponent({super.position})
: super(
size: Vector2.all(50),
anchor: Anchor.center,
removeOnFinish: true,
);
@override
Future<void> onLoad() async {
animation = await game.loadSpriteAnimation(
'flame_benchmark/rogue_shooter/explosion.png',
SpriteAnimationData.sequenced(
stepTime: 0.1,
amount: 6,
textureSize: Vector2.all(32),
loop: false,
),
);
}
}

View File

@ -0,0 +1,70 @@
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'bullet_component.dart';
import 'enemy_component.dart';
import 'explosion_component.dart';
class PlayerComponent extends SpriteAnimationComponent
with HasGameRef, CollisionCallbacks {
late TimerComponent bulletCreator;
PlayerComponent() : super(size: Vector2(50, 75), anchor: Anchor.center);
@override
Future<void> onLoad() async {
position = game.size / 2;
add(CircleHitbox());
add(
bulletCreator = TimerComponent(
period: 0.05,
repeat: true,
autoStart: false,
onTick: _createBullet,
),
);
animation = await game.loadSpriteAnimation(
'flame_benchmark/rogue_shooter/player.png',
SpriteAnimationData.sequenced(
stepTime: 0.2,
amount: 4,
textureSize: Vector2(32, 39),
),
);
}
final _bulletAngles = [0.5, 0.3, 0.0, -0.5, -0.3];
void _createBullet() {
game.addAll(
_bulletAngles.map(
(angle) => BulletComponent(
position: position + Vector2(0, -size.y / 2),
angle: angle,
),
),
);
}
void beginFire() {
bulletCreator.timer.start();
}
void stopFire() {
bulletCreator.timer.pause();
}
void takeHit() {
game.add(ExplosionComponent(position: position));
}
@override
void onCollisionStart(
Set<Vector2> intersectionPoints,
PositionComponent other,
) {
super.onCollisionStart(intersectionPoints, other);
if (other is EnemyComponent) {
other.takeHit();
}
}
}

View File

@ -0,0 +1,66 @@
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/sprite.dart';
import 'star_component.dart';
class StarBackGroundCreator extends Component with HasGameRef {
final gapSize = 12;
late final SpriteSheet spriteSheet;
Random random = Random();
StarBackGroundCreator();
@override
Future<void> onLoad() async {
spriteSheet = SpriteSheet.fromColumnsAndRows(
image: await game.images.load('flame_benchmark/rogue_shooter/stars.png'),
rows: 4,
columns: 4,
);
final starGapTime = (game.size.y / gapSize) / StarComponent.speed;
add(
TimerComponent(
period: starGapTime,
repeat: true,
onTick: () => _createRowOfStars(0),
),
);
_createInitialStars();
}
void _createStarAt(double x, double y) {
final animation = spriteSheet.createAnimation(
row: random.nextInt(3),
to: 4,
stepTime: 0.1,
)..variableStepTimes = [max(20, 100 * random.nextDouble()), 0.1, 0.1, 0.1];
game.add(StarComponent(animation: animation, position: Vector2(x, y)));
}
void _createRowOfStars(double y) {
const gapSize = 6;
final starGap = game.size.x / gapSize;
for (var i = 0; i < gapSize; i++) {
_createStarAt(
starGap * i + (random.nextDouble() * starGap),
y + (random.nextDouble() * 20),
);
}
}
void _createInitialStars() {
final rows = game.size.y / gapSize;
for (var i = 0; i < gapSize; i++) {
_createRowOfStars(i * rows);
}
}
}

View File

@ -0,0 +1,17 @@
import 'package:flame/components.dart';
class StarComponent extends SpriteAnimationComponent with HasGameRef {
static const speed = 10;
StarComponent({super.animation, super.position})
: super(size: Vector2.all(20));
@override
void update(double dt) {
super.update(dt);
y += dt * speed;
if (y >= game.size.y) {
removeFromParent();
}
}
}

View File

@ -0,0 +1,7 @@
// import 'package:flame/game.dart';
// import 'package:flutter/widgets.dart';
// import 'package:rogue_shooter/rogue_shooter_game.dart';
// void main() {
// runApp(GameWidget(game: RogueShooterGame()));
// }

View File

@ -0,0 +1,36 @@
import 'dart:ui';
// Source: https://github.com/flame-engine/flame/blob/main/packages/flame/lib/src/game/mixins/has_performance_tracker.dart#L6
import 'package:flame/game.dart';
/// A mixin that adds performance tracking to a game.
mixin HasPerformanceTracker on Game {
int _updateTime = 0;
int _renderTime = 0;
final _stopwatch = Stopwatch();
/// The time it took to update the game in milliseconds.
int get updateTime => _updateTime;
/// The time it took to render the game in milliseconds.
int get renderTime => _renderTime;
@override
void update(double dt) {
_stopwatch.reset();
_stopwatch.start();
super.update(dt);
_stopwatch.stop();
_updateTime = _stopwatch.elapsedMilliseconds;
}
@override
void render(Canvas canvas) {
_stopwatch.reset();
_stopwatch.start();
super.render(canvas);
_stopwatch.stop();
_renderTime = _stopwatch.elapsedMilliseconds;
}
}

View File

@ -0,0 +1,93 @@
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/game.dart';
import 'components/enemy_creator.dart';
import 'components/player_component.dart';
import 'components/star_background_creator.dart';
import 'mixins/has_performance_tracker.dart';
class RogueShooterGame extends FlameGame
with PanDetector, HasCollisionDetection, HasPerformanceTracker {
static const String description = '''
A simple space shooter game used for testing performance of the collision
detection system in Flame.
''';
late final PlayerComponent _player;
late final TextComponent _componentCounter;
late final TextComponent _scoreText;
final _updateTime = TextComponent(
text: 'Update time: 0ms',
position: Vector2(90, 20),
priority: 1,
);
final TextComponent _renderTime = TextComponent(
text: 'Render time: 0ms',
position: Vector2(90, 45),
priority: 1,
);
int _score = 0;
@override
Future<void> onLoad() async {
add(_player = PlayerComponent());
addAll([
FpsTextComponent(
position: size - Vector2(0, 50),
anchor: Anchor.bottomRight,
),
_scoreText = TextComponent(
position: size - Vector2(0, 25),
anchor: Anchor.bottomRight,
priority: 1,
),
_componentCounter = TextComponent(
position: size,
anchor: Anchor.bottomRight,
priority: 1,
),
]);
add(EnemyCreator());
add(StarBackGroundCreator());
addAll([_updateTime, _renderTime]);
}
@override
void update(double dt) {
super.update(dt);
_scoreText.text = 'Score: $_score';
_componentCounter.text = 'Components: ${children.length}';
_updateTime.text = 'Update time: $updateTime ms';
_renderTime.text = 'Render time: $renderTime ms';
}
@override
void onPanStart(_) {
_player.beginFire();
}
@override
void onPanEnd(_) {
_player.stopFire();
}
@override
void onPanCancel() {
_player.stopFire();
}
@override
void onPanUpdate(DragUpdateInfo info) {
_player.position += info.delta.global;
}
void increaseScore() {
_score++;
}
}

View File

@ -0,0 +1,18 @@
import 'package:flame/game.dart';
import 'package:flutter/widgets.dart';
import 'rogue_shooter_game.dart';
class RogueShooterWidget extends StatelessWidget {
const RogueShooterWidget({super.key});
@override
Widget build(BuildContext context) {
return GameWidget(
game: RogueShooterGame(),
loadingBuilder: (_) => const Center(
child: Text('Loading'),
),
);
}
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Blue Fire
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,12 @@
# Flame Engine Sprite Renderer Benchmark
This is a simple benchmark to test the render
performance of the Flutter Render Engine.
It was modified from: https://github.com/flame-engine/flame/blob/main/examples/lib/stories/animations/benchmark_example.dart
## Modifications
- added framerate display
- add sprites automatically for 30 minutes and only if the framerate is above 59
- timer and benchmark test starts automatically and displays the result after 30 minutes

View File

@ -0,0 +1,14 @@
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'sprite_benchmark.dart';
class BenchmarkWorld extends World
with TapCallbacks, HasGameReference<SpriteBenchmark> {
final Random random = Random();
@override
void onTapDown(TapDownEvent event) {}
}

View File

@ -0,0 +1,144 @@
import 'dart:collection';
import 'dart:io';
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/text.dart';
import 'package:flutter/material.dart';
import '../../../data/models/ember.dart';
import 'benchmark_world.dart';
class SpriteBenchmark extends FlameGame with HasGameRef {
static const description = '''
See how many SpriteAnimationComponent's your platform can handle before it
starts to drop in FPS, this is without any sprite batching and such.
''';
SpriteBenchmark() : super(world: BenchmarkWorld());
// 30 minutes countdown
final countdown = Timer(1800);
final emberSize = Vector2.all(20);
late final TextComponent emberCounter;
final counterPrefix = 'Animations: ';
double posX = 0;
double posY = 0;
final Queue<double> window = Queue();
@override
Color backgroundColor() => Colors.black;
double _sum = 0;
int spriteCount = 0;
final int windowSize = 60;
TextPaint textPaint = TextPaint(
style: TextStyle(
fontSize: 20.0,
fontFamily: 'Awesome Font',
background: Paint()
..color = Colors.indigo
..strokeJoin = StrokeJoin.round
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 25.0,
),
);
@override
Future<void> onLoad() async {
await camera.viewport.addAll([
FpsTextComponent(
position: size - Vector2(20, 80),
anchor: Anchor.bottomRight,
textRenderer: textPaint,
),
emberCounter = TextComponent(
position: size - Vector2(20, 25),
anchor: Anchor.bottomRight,
priority: 1,
textRenderer: textPaint,
),
]);
// world.add(Ember(size: emberSize));
children.register<Ember>();
}
double get fps {
return window.isEmpty ? 0 : window.length / _sum;
}
@override
void update(double dt) async {
// add text to the screen only once
if (countdown.finished) {
showDialog(
context: gameRef.buildContext!,
builder: (context) {
return AlertDialog(
title: const Text('Benchmark finished'),
content: Text(
'After 30 minutes, the benchmark finished with $spriteCount sprite animations.',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
},
child: const Text('Close'),
),
],
);
},
);
countdown.reset();
}
if (!countdown.finished && countdown.isRunning()) {
countdown.update(dt);
window.addLast(dt);
_sum += dt;
if (window.length > windowSize) {
_sum -= window.removeFirst();
}
if (fps > 59) {
if (posY > size.y) {
final Random random = Random();
add(
Ember(
size: emberSize,
position: Vector2(
(size.x) * random.nextDouble() * (random.nextBool() ? 1 : -1),
(size.y) * random.nextDouble() * (random.nextBool() ? 1 : -1),
),
),
);
sleep(const Duration(milliseconds: 50));
} else {
for (int i = 0; i < 1; i++) {
add(Ember(
size: emberSize,
position: Vector2(10 + posX, 10 + posY),
));
if (posX < size.x) {
posX += 20;
} else {
posY += 20;
posX = 0;
}
}
sleep(const Duration(milliseconds: 50));
}
spriteCount += 1;
}
super.update(dt);
emberCounter.text = '$counterPrefix $spriteCount';
}
}
}

View File

@ -0,0 +1,31 @@
import '../../../common/routing/routes.dart';
import 'models/benchmark.dart';
final List<Benchmark> benchmarksList = [
Benchmark(
title: 'Flame Sprite Rendering',
desc: 'Tests how many sprites can be renderer without a frame drop.',
route: RoutesBenchmark.sprites,
),
Benchmark(
title: 'Rogue Shooter Benchmark',
desc:
'A simple scrolling shooter game which is used by the Flame Engines team for testing the performance of Flame.',
route: RoutesBenchmark.rogueShooter,
),
Benchmark(
title: 'VSync Test',
desc: 'Tests if the VSync is working properly with a fragment shader.',
route: RoutesBenchmark.shader,
),
Benchmark(
title: 'Video Benchmark',
desc: 'Video Player demo.',
route: RoutesBenchmark.video,
),
Benchmark(
title: 'Simple Tiles Map Benchmark',
desc: 'Map demo.',
route: RoutesBenchmark.map,
),
];

View File

@ -0,0 +1,15 @@
import '../../../../common/routing/routes.dart';
class Benchmark {
final String title;
final String desc;
final String buttonText;
final RoutesBenchmark route;
Benchmark({
required this.title,
required this.desc,
this.buttonText = 'Go to benchmark',
required this.route,
});
}

View File

@ -0,0 +1,25 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:meta/meta.dart';
class Ember<T extends FlameGame> extends SpriteAnimationComponent
with HasGameReference<T> {
Ember({super.position, Vector2? size, super.priority, super.key})
: super(
size: size ?? Vector2.all(50),
anchor: Anchor.center,
);
@mustCallSuper
@override
Future<void> onLoad() async {
animation = await game.loadSpriteAnimation(
'flame_benchmark/sprite_benchmark/ember.png',
SpriteAnimationData.sequenced(
amount: 3,
textureSize: Vector2.all(16),
stepTime: 0.15,
),
);
}
}

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import '../data/benchmarks_list.dart';
import 'widgets/benchmark_card.dart';
class BenchmarkScreen extends StatelessWidget {
const BenchmarkScreen({super.key});
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: const EdgeInsets.all(12),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).size.shortestSide < 600 ? 2 : 3,
crossAxisSpacing: 12.0,
mainAxisSpacing: 12,
),
itemCount: benchmarksList.length,
itemBuilder: (context, index) {
return BenchmarkCard(
benchmark: benchmarksList[index],
);
},
);
}
}

View File

@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:flutter_elinux/common/widgets/floating_menu_button.dart';
import 'package:flutter_elinux/common/widgets/scaffold_with_statusbar.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:simple_tiles_map/simple_tiles_map.dart';
class MapBenchmarkScreen extends StatefulWidget {
const MapBenchmarkScreen({super.key});
@override
State<MapBenchmarkScreen> createState() => _MapBenchmarkScreenState();
}
class _MapBenchmarkScreenState extends State<MapBenchmarkScreen> {
TypeMap _typeMap = TypeMap.osm;
@override
Widget build(BuildContext context) {
MapOptions mapOptions = MapOptions(
maxZoom: 19,
minZoom: 5,
);
return ScaffoldWithStatusbar(
body: Stack(children: [
Column(
children: [
SimpleTilesMap(
typeMap: _typeMap,
mapOptions: mapOptions,
),
],
),
FloatingMenuButton(
popupMenuButton: PopupMenuButton(
itemBuilder: (BuildContext context) {
return [
PopupMenuItem(
value: TypeMap.google,
enabled: (_typeMap != TypeMap.google),
child: const Text('Google'),
),
PopupMenuItem(
value: TypeMap.googleHybrid,
enabled: (_typeMap != TypeMap.googleHybrid),
child: const Text('Google Hybrid'),
),
PopupMenuItem(
value: TypeMap.googleSatellite,
enabled: (_typeMap != TypeMap.googleSatellite),
child: const Text('Google Satellite'),
),
PopupMenuItem(
value: TypeMap.osm,
enabled: (_typeMap != TypeMap.osm),
child: const Text('Open Street Map'),
),
PopupMenuItem(
value: TypeMap.cartoMapPositron,
enabled: (_typeMap != TypeMap.cartoMapPositron),
child: const Text('Carto Map Positron'),
),
PopupMenuItem(
value: TypeMap.cartoMapDark,
enabled: (_typeMap != TypeMap.cartoMapDark),
child: const Text('Carto Map Dark'),
),
PopupMenuItem(
value: TypeMap.esriSatellite,
enabled: (_typeMap != TypeMap.esriSatellite),
child: const Text('Esri Satellite'),
),
PopupMenuItem(
value: TypeMap.esriStreets,
enabled: (_typeMap != TypeMap.esriStreets),
child: const Text('Esri Streets'),
),
PopupMenuItem(
value: TypeMap.esriTopo,
enabled: (_typeMap != TypeMap.esriTopo),
child: const Text('Esri Topo'),
),
];
},
onSelected: (dynamic value) {
setState(() {
_typeMap = value;
});
},
),
),
]),
);
}
}

View File

@ -0,0 +1,24 @@
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import '../../../common/widgets/floating_menu_button.dart';
import '../business/flame/rogue_shooter/rogue_shooter_game.dart';
class RogueShooterScreen extends StatefulWidget {
const RogueShooterScreen({super.key});
@override
State<RogueShooterScreen> createState() => _RogueShooterScreenState();
}
class _RogueShooterScreenState extends State<RogueShooterScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
GameWidget(game: RogueShooterGame()),
const FloatingMenuButton(),
]),
);
}
}

View File

@ -0,0 +1,21 @@
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import '../../../common/widgets/floating_menu_button.dart';
import '../business/flame/sprite_renderer/sprite_benchmark.dart';
class SpriteBenchmarkScreen extends StatelessWidget {
const SpriteBenchmarkScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
GameWidget(
game: SpriteBenchmark(),
),
const FloatingMenuButton(),
]),
);
}
}

View File

@ -0,0 +1,111 @@
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoBenchmarkScreen extends StatefulWidget {
const VideoBenchmarkScreen({super.key});
@override
State<VideoBenchmarkScreen> createState() => _VideoBenchmarkScreenState();
}
class _VideoBenchmarkScreenState extends State<VideoBenchmarkScreen> {
late VideoPlayerController _controller;
String selectedMode = '720p';
@override
void initState() {
super.initState();
_controller = VideoPlayerController.asset(
'assets/video/video_720p.mp4',
)
..initialize().then((_) {
setState(() {});
})
..setLooping(true);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Video Player Demo ($selectedMode)'),
actions: [
ElevatedButton(
onPressed: () {
_controller = VideoPlayerController.asset(
'assets/video/video_720p.mp4',
)
..initialize().then((_) {
setState(() {
selectedMode = '720p';
});
})
..setLooping(true);
},
child: const Text('load 720p')),
const SizedBox(
width: 8,
),
ElevatedButton(
onPressed: () {
_controller = VideoPlayerController.asset(
'assets/video/video_1080p.mp4',
)
..initialize().then((_) {
setState(() {
selectedMode = '1080p';
});
})
..setLooping(true);
},
child: const Text('load 1080p')),
const SizedBox(
width: 8,
),
ElevatedButton(
onPressed: () {
_controller = VideoPlayerController.asset(
'assets/video/video_2160p.mp4',
)
..initialize().then((_) {
setState(() {
selectedMode = '2160p';
});
})
..setLooping(true);
},
child: const Text('load 2160p')),
const SizedBox(
width: 8,
),
],
),
body: Center(
child: _controller.value.isInitialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
: Container(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
);
}
}

View File

@ -0,0 +1,96 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import '../../../common/widgets/floating_menu_button.dart';
class VsyncBenchmarkScreen extends StatelessWidget {
const VsyncBenchmarkScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Stack(
children: [
SizedBox(
width: double.infinity,
height: double.infinity,
child: ShaderHomePage(),
),
FloatingMenuButton(),
],
),
);
}
}
class ShaderHomePage extends StatefulWidget {
const ShaderHomePage({super.key});
@override
State<ShaderHomePage> createState() => _ShaderHomePageState();
}
class _ShaderHomePageState extends State<ShaderHomePage>
with SingleTickerProviderStateMixin {
double delta = 0;
FragmentShader? shader;
late final Ticker _ticker;
void loadMyShader() async {
var program = await FragmentProgram.fromAsset('shaders/vsync.frag');
shader = program.fragmentShader();
}
@override
void initState() {
super.initState();
loadMyShader();
_ticker = createTicker((elapsed) {
setState(() {
delta += 1 / 60;
});
});
_ticker.start();
}
@override
void dispose() {
_ticker.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (shader == null) {
return const Center(child: CircularProgressIndicator());
} else {
return Container(
color: Colors.white,
child: CustomPaint(painter: ShaderPainter(shader!, delta)));
}
}
}
class ShaderPainter extends CustomPainter {
final FragmentShader shader;
final double time;
ShaderPainter(FragmentShader fragmentShader, this.time)
: shader = fragmentShader;
@override
void paint(Canvas canvas, Size size) {
final paint = Paint();
shader.setFloat(0, size.width);
shader.setFloat(1, size.height);
shader.setFloat(2, time);
paint.shader = shader;
canvas.drawRect(Offset.zero & size, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../data/models/benchmark.dart';
class BenchmarkCard extends StatelessWidget {
const BenchmarkCard({
required this.benchmark,
super.key,
});
final Benchmark benchmark;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
benchmark.title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 18),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
benchmark.desc,
textAlign: TextAlign.center,
),
),
TextButton(
onPressed: () {
context.pushNamed(benchmark.route.name);
},
child: Text(benchmark.buttonText),
),
],
),
),
);
}
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
'Flutter on Embedded Linux',
style: Theme.of(context).textTheme.headlineLarge,
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,365 @@
import 'package:flutter/material.dart';
import 'widgets/component_decoration.dart';
// https://github.com/flutter/samples/blob/main/material_3_demo/lib/component_screen.dart
const rowDivider = SizedBox(width: 20);
const colDivider = SizedBox(height: 10);
const tinySpacing = 3.0;
const smallSpacing = 10.0;
const double cardWidth = 115;
const double widthConstraint = 450;
class MaterialDemoScreen extends StatefulWidget {
const MaterialDemoScreen({super.key});
@override
State<MaterialDemoScreen> createState() => _MaterialDemoScreenState();
}
class _MaterialDemoScreenState extends State<MaterialDemoScreen> {
bool switch1 = false;
bool switch2 = true;
double sliderValue1 = 0.2;
double sliderValue2 = 0.5;
int navBarIndex = 0;
bool indicatorPlayAnimation = false;
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
const SizedBox(
height: 84,
),
ComponentDecoration(
title: 'Common Buttons',
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {},
child: const Text('Elevated'),
),
rowDivider,
ElevatedButton.icon(
onPressed: () {},
icon: const Icon(Icons.add),
label: const Text('Icon'),
),
rowDivider,
const ElevatedButton(
style: ButtonStyle(),
onPressed: null,
child: Text('Elevated'),
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FilledButton(
onPressed: () {},
child: const Text('Filled'),
),
rowDivider,
FilledButton.icon(
onPressed: () {},
icon: const Icon(Icons.add),
label: const Text('Icon'),
),
rowDivider,
const FilledButton(
style: ButtonStyle(),
onPressed: null,
child: Text('Filled'),
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {},
child: const Text('Text'),
),
rowDivider,
TextButton.icon(
onPressed: () {},
icon: const Icon(Icons.add),
label: const Text('Icon'),
),
rowDivider,
const TextButton(
style: ButtonStyle(),
onPressed: null,
child: Text('Text'),
),
],
),
],
),
),
ComponentDecoration(
title: 'Switches',
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Switch(
value: switch1,
onChanged: (bool value) {
setState(() {
switch1 = value;
});
},
),
Switch(
value: switch2,
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return const Icon(Icons.check);
}
return null; // All other states will use the default thumbIcon.
}),
onChanged: (bool value) {
setState(() {
switch2 = value;
});
},
),
],
),
),
ComponentDecoration(
title: 'Floating Action Buttons',
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
FloatingActionButton.small(
onPressed: () {},
tooltip: 'Small',
child: const Icon(Icons.add),
),
FloatingActionButton(
onPressed: () {},
tooltip: 'Standard',
child: const Icon(Icons.add),
),
FloatingActionButton.extended(
onPressed: () {},
tooltip: 'Extended',
icon: const Icon(Icons.add),
label: const Text('Create'),
),
FloatingActionButton.large(
onPressed: () {},
tooltip: 'Large',
child: const Icon(Icons.add),
),
],
),
),
ComponentDecoration(
title: 'Sliders',
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Slider(
value: sliderValue1,
onChanged: (double value) {
setState(() {
sliderValue1 = value;
});
},
),
Slider(
value: sliderValue2,
onChanged: (double value) {
setState(() {
sliderValue2 = value;
});
},
divisions: 5,
),
],
),
),
ComponentDecoration(
title: 'Loading Indicators',
child: Column(
children: [
IconButton(
onPressed: () {
setState(() {
indicatorPlayAnimation = !indicatorPlayAnimation;
});
},
icon: indicatorPlayAnimation
? const Icon(Icons.pause)
: const Icon(Icons.play_arrow),
),
colDivider,
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
rowDivider,
CircularProgressIndicator(
value: indicatorPlayAnimation ? null : 0.5,
),
rowDivider,
Expanded(
child: LinearProgressIndicator(
value: indicatorPlayAnimation ? null : 0.5,
),
),
rowDivider,
],
),
],
),
),
ComponentDecoration(
title: 'Dialogs and BottomSheets',
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
// https://api.flutter.dev/flutter/material/showModalBottomSheet.html
return SizedBox(
height: 200,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
),
],
),
),
);
},
);
},
child: const Text('show ModalBottomSheet'),
),
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Dialog Title'),
content: const Text('Dialog description'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
),
],
);
},
);
},
child: const Text('show Dialog'),
),
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog.fullscreen(
child: Scaffold(
appBar: AppBar(
title: const Text('Fullscreen Dialog'),
),
body: Center(
child: ElevatedButton(
child: const Text('Close Dialog'),
onPressed: () => Navigator.pop(context),
),
),
),
);
},
);
},
child: const Text('show Fullscreen Dialog'),
),
],
),
),
ComponentDecoration(
title: 'Navigation Bar',
child: NavigationBar(
selectedIndex: navBarIndex,
onDestinationSelected: (value) {
setState(() {
navBarIndex = value;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.favorite),
label: 'Favorites',
),
NavigationDestination(
icon: Icon(Icons.search),
label: 'Search',
),
NavigationDestination(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
),
),
ComponentDecoration(
title: 'Bottom App Bar',
child: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.menu),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.search),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.more_vert),
),
],
),
),
),
],
),
);
}
}

View File

@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
class ComponentDecoration extends StatelessWidget {
const ComponentDecoration(
{required this.child, required this.title, super.key});
final Widget child;
final String title;
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(title),
const SizedBox(width: 8),
const Icon(
Icons.info_outline,
size: 16,
),
],
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).colorScheme.outlineVariant,
),
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 5.0,
vertical: 20.0,
),
child: child,
),
),
),
],
);
}
}

View File

@ -0,0 +1,28 @@
import 'package:dart_periphery/dart_periphery.dart';
import 'package:flutter/foundation.dart';
// linux_spidev was tried out here beforehand,
// but it didn't work and was replaced by dart_periphery
// import 'package:linux_spidev/linux_spidev.dart';
class MatrixService {
final spi = SPI(0, 0, SPImode.mode0, 35000);
// final spidev = Spidev.fromBusDevNrs(0, 0);
void setLedsOn(List<int> pixels) async {
// final handle = spidev.open();
try {
int refresh = 1;
while (refresh != 0) {
spi.transfer(pixels, false);
await Future.delayed(const Duration(milliseconds: 100));
refresh--;
}
} catch (e) {
debugPrint(e.toString());
} finally {
// clean up the resource
spi.dispose();
}
}
}

View File

@ -0,0 +1,28 @@
import 'dart:ui';
int findClosestColorIndex(Color sourceColor) {
int bestMatchIndex = 0;
double smallestDistance = double.infinity; // Start with a 'large' distance
// Iterate through possible controller colors (0-255)
for (int i = 0; i < 256; i++) {
// Example: Assume your controller just uses an even R/G/B scaling
int r = (i >> 5) * 32; // Assuming an even split to get 8 values
int g = ((i >> 2) & 0x7) * 32;
int b = (i & 0x3) * 64;
// Color controllerColor = Color.fromARGB(255, r, g, b);
// Calculate distance (you can use different formulas)
int distance = (sourceColor.red - r) * (sourceColor.red - r) +
(sourceColor.green - g) * (sourceColor.green - g) +
(sourceColor.blue - b) * (sourceColor.blue - b);
if (distance < smallestDistance) {
smallestDistance = distance.toDouble();
bestMatchIndex = i;
}
}
return bestMatchIndex;
}

View File

@ -0,0 +1,11 @@
import 'dart:ui';
class Pixel {
final Offset offset;
final Color color;
Pixel({
required this.offset,
required this.color,
});
}

View File

@ -0,0 +1,143 @@
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart';
import '../data/models/pixel.dart';
import 'widgets/pixel_painter.dart';
class MatrixScreen extends StatefulWidget {
const MatrixScreen({super.key});
@override
State<MatrixScreen> createState() => _MatrixScreenState();
}
class _MatrixScreenState extends State<MatrixScreen> {
List<Pixel> pixels = List.generate(
64,
(index) => Pixel(
offset: Offset.zero,
color: Colors.transparent,
),
);
Color color = Colors.green;
void _updatePixel(Offset offset) {
const double cellSize = 50; // Size of each grid cell
// Get the row and column based on touch position
int row = (offset.dy / cellSize).floor();
int col = (offset.dx / cellSize).floor();
// Bound Check (Only proceed if within the grid)
if (row >= 0 && row < 8 && col >= 0 && col < 8) {
// Calculate the index in the flat list
int index = row * 8 + col;
// Update the Pixel
pixels[index] = Pixel(offset: offset, color: color);
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Listener(
onPointerDown: (event) {
// Determine offset
Offset offset =
Offset(event.localPosition.dx, event.localPosition.dy);
// Calculate index and update pixel
_updatePixel(offset);
},
onPointerMove: (event) {
// Determine offset
Offset offset =
Offset(event.localPosition.dx, event.localPosition.dy);
// Calculate index and update pixel
_updatePixel(offset);
},
child: InteractiveViewer(
maxScale: 5,
child: Container(
// color: Colors.white,
decoration: BoxDecoration(border: Border.all()),
child: ClipRRect(
child: GridPaper(
divisions: 2,
interval: 500,
color: Theme.of(context).textTheme.headlineLarge?.color ??
Colors.red,
child: Transform(
transform: Matrix4.identity()..scale(50.0),
child: ClipRect(
child: CustomPaint(
size: const Size(400, 400),
painter: PixelPainter(
pixels: pixels,
),
),
),
),
),
),
),
),
),
const SizedBox(
width: 24,
),
ColorIndicator(
width: 40,
height: 40,
borderRadius: 24,
color: color,
elevation: 1,
onSelectFocus: false,
onSelect: () async {
// Wait for the dialog to return color selection result.
final Color newColor = await showColorPickerDialog(
context,
color,
title: Text('ColorPicker',
style: Theme.of(context).textTheme.titleLarge),
width: 40,
height: 40,
spacing: 0,
runSpacing: 0,
borderRadius: 0,
wheelDiameter: 165,
enableOpacity: true,
showColorCode: true,
colorCodeHasColor: true,
pickersEnabled: <ColorPickerType, bool>{
ColorPickerType.wheel: true,
},
copyPasteBehavior: const ColorPickerCopyPasteBehavior(
copyButton: true,
pasteButton: true,
longPressMenu: true,
),
actionButtons: const ColorPickerActionButtons(
okButton: true,
closeButton: true,
dialogActionButtons: false,
),
constraints: const BoxConstraints(
minHeight: 480, minWidth: 320, maxWidth: 320),
);
// Update the state with the newly selected color from the color picker
setState(() {
color = newColor;
});
}),
],
),
);
}
}

View File

@ -0,0 +1,63 @@
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import '../../data/models/pixel.dart';
import '../../business/service/matrix_service.dart';
import '../../business/utils/find_closest_color_index.dart';
class PixelPainter extends CustomPainter {
final List<Pixel> pixels;
PixelPainter({required this.pixels});
@override
void paint(Canvas canvas, Size size) {
const double cellSize = 50;
if (pixels.isNotEmpty) {
for (final pixel in pixels) {
final Offset roundedOffset = Offset(
(pixel.offset.dx / cellSize).clamp(0, 7).toInt() * cellSize + 25,
(pixel.offset.dy / cellSize).clamp(0, 7).toInt() * cellSize + 25,
);
canvas.drawPoints(
PointMode.points,
[roundedOffset / 50],
Paint()
..color = pixel.color
..strokeWidth = 1
..isAntiAlias = false
..strokeJoin = StrokeJoin.round
..style = PaintingStyle.stroke,
);
}
// Assuming 'width' represents the width of your matrix (in this case, 8)
const width = 8;
List<int> colorIntList = pixels.map((pixel) {
if (pixel.color.value != 0) return findClosestColorIndex(pixel.color);
return pixel.color.value;
}).toList();
// horizontal mirroring
List<int> mirroredList = List.generate(64, (index) {
int row = index ~/ width;
int reversedCol = width - 1 - (index % width);
int newIndex = row * width + reversedCol;
return colorIntList[newIndex];
});
// dart periphery is only supported on linux
if (Platform.isLinux) {
MatrixService().setLedsOn(mirroredList.reversed.toList());
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

View File

@ -0,0 +1,102 @@
import 'package:flutter_gpiod/flutter_gpiod.dart';
class MorseService {
final _chips = FlutterGpiod.instance.chips;
printMessage(String message) async {
final chip = _getRaspberryGPIO();
final ledPin = chip.lines[18];
ledPin.requestOutput(consumer: "morse code led", initialValue: false);
final List<String> chars = message.toUpperCase().split('');
const int pulseDuration = 100;
for (var char in chars) {
final List<String> morseCode = _morseAlphabet[char]?.split('') ?? ['*'];
for (var code in morseCode) {
print('CODE: $code from $morseCode');
if (code == '*') {
await Future.delayed(const Duration(milliseconds: pulseDuration * 7));
break;
}
ledPin.setValue(true);
print('ON');
if (code == '.') {
await Future.delayed(const Duration(milliseconds: pulseDuration));
} else if (code == '-') {
await Future.delayed(const Duration(milliseconds: pulseDuration * 3));
}
print('OFF');
ledPin.setValue(false);
// pause between letters
await Future.delayed(const Duration(milliseconds: pulseDuration));
}
print("SPACE BETWEEN LETTERS");
// space between letters is 3*pulseDuration
await Future.delayed(const Duration(milliseconds: pulseDuration * 3));
}
ledPin.release();
return true;
}
GpioChip _getRaspberryGPIO() {
/// Retrieve the line with index 23 of the first chip.
/// This is BCM pin 23 for the Raspberry Pi.
///
/// I recommend finding the chip you want
/// based on the chip label, as is done here.
///
/// In this example, we search for the main Raspberry Pi GPIO chip and then
/// retrieve the line with index 23 of it. So [line] is GPIO pin BCM 23.
///
/// The main GPIO chip is called `pinctrl-bcm2711` on Pi 4 and `pinctrl-bcm2835`
/// on older Raspberry Pis and it was also called that way on Pi 4 with older
/// kernel versions.
final chip = _chips.singleWhere(
(chip) => chip.label == 'pinctrl-bcm2711',
orElse: () =>
_chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835'),
);
return chip;
}
// International Morse Code
final Map<String, String> _morseAlphabet = {
'A': '.-',
'B': '-...',
'C': '-.-.',
'D': '-..',
'E': '.',
'F': '..-.',
'G': '--.',
'H': '....',
'I': '..',
'J': '.---',
'K': '-.-',
'L': '.-..',
'M': '--',
'N': '-.',
'O': '---',
'P': '.--.',
'Q': '--.-',
'R': '.-.',
'S': '...',
'T': '-',
'U': '..-',
'V': '...-',
'W': '.--',
'X': '-..-',
'Y': '-.--',
'Z': '--..',
'0': '-----',
'1': '.----',
'2': '..---',
'3': '...--',
'4': '....-',
'5': '.....',
'6': '-....',
'7': '--...',
'8': '---..',
'9': '----.',
};
}

View File

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../common/widgets/text_input_dialog.dart';
import '../business/service/morse_service.dart';
class LedScreen extends ConsumerWidget {
const LedScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final TextEditingController textController = TextEditingController();
return Center(
child: SizedBox(
height: 224,
child: Card(
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3),
elevation: 0,
margin: const EdgeInsets.all(32),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
constraints: const BoxConstraints(maxWidth: 400),
child: TextInputDialog(
controller: textController,
title: 'Enter Morse Code Message',
onReturnPress: () {
// FocusScope.of(context).unfocus();
context.pop();
},
),
),
),
const SizedBox(
height: 12,
),
ElevatedButton(
onPressed: () {
MorseService().printMessage(textController.text);
},
child: const Text("Morse Code"),
),
],
),
),
),
),
);
}
}

View File

@ -0,0 +1,45 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../data/repository/news_repository.dart';
import '../data/models/region_news.dart';
part 'news_controller.g.dart';
/// The news controller
@riverpod
class NewsController extends _$NewsController {
@override
Future<RegionNews> build() async {
try {
final String newsResponse = await NewsRepository().fetchNews();
final Map<String, dynamic> jsonData = json.decode(newsResponse);
final RegionNews news = RegionNews.fromJson(jsonData);
return news;
} catch (e) {
rethrow;
}
}
/// Append the next page of news to the current news list
Future<void> appendPage() async {
try {
final RegionNews currentNews =
state.asData?.value ?? const RegionNews(news: []);
final newsResponse =
await NewsRepository().fetchNewsPage(currentNews.nextPage ?? 'null');
final Map<String, dynamic> jsonData = jsonDecode(newsResponse);
final RegionNews nextNews = RegionNews.fromJson(jsonData);
final RegionNews appendNews =
currentNews.copyWith(nextPage: nextNews.nextPage, news: [
...currentNews.news,
...nextNews.news,
]);
state = AsyncData(appendNews);
} catch (e) {
debugPrint(e.toString());
}
}
}

View File

@ -0,0 +1,28 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'news_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$newsControllerHash() => r'd2b544008f72eeee9a71bec7d0fe5b8981536fee';
/// The news controller
///
/// Copied from [NewsController].
@ProviderFor(NewsController)
final newsControllerProvider =
AutoDisposeAsyncNotifierProvider<NewsController, RegionNews>.internal(
NewsController.new,
name: r'newsControllerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$newsControllerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$NewsController = AutoDisposeAsyncNotifier<RegionNews>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -0,0 +1,15 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'scroll_state_controller.g.dart';
@riverpod
class ScrollStateController extends _$ScrollStateController {
@override
bool build() {
return false;
}
toggle(bool set) {
state = set;
}
}

View File

@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'scroll_state_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$scrollStateControllerHash() =>
r'0fd7eab4471ba8adac0d631c0580afb88e31901d';
/// See also [ScrollStateController].
@ProviderFor(ScrollStateController)
final scrollStateControllerProvider =
AutoDisposeNotifierProvider<ScrollStateController, bool>.internal(
ScrollStateController.new,
name: r'scrollStateControllerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$scrollStateControllerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ScrollStateController = AutoDisposeNotifier<bool>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@ -0,0 +1,134 @@
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'teaser_image.dart';
part 'news.freezed.dart';
part 'news.g.dart';
@freezed
class News with _$News {
const factory News({
String? title,
TeaserImage? teaserImage,
TeaserImage? brandingImage,
String? date,
String? type,
}) = _News;
factory News.fromJson(Map<String, dynamic> json) => _$NewsFromJson(json);
}
// old manual implementation:
// final String sophoraId;
// final String externalId;
// final String title;
// final TeaserImage? teaserImage;
// final TeaserImage? brandingImage;
// final String date;
// final String updateCheckUrl;
// final int regionId;
// final String details;
// final String detailsweb;
// final String shareURL;
// final String topline;
// final String firstSentence;
// final String type;
// News({
// required this.sophoraId,
// required this.externalId,
// required this.title,
// this.teaserImage,
// this.brandingImage,
// required this.date,
// required this.updateCheckUrl,
// required this.regionId,
// required this.details,
// required this.detailsweb,
// required this.shareURL,
// required this.topline,
// required this.firstSentence,
// required this.type,
// });
// News copyWith({
// String? sophoraId,
// String? externalId,
// String? title,
// TeaserImage? teaserImage,
// TeaserImage? brandingImage,
// String? date,
// String? updateCheckUrl,
// int? regionId,
// String? details,
// String? detailsweb,
// String? shareURL,
// String? topline,
// String? firstSentence,
// String? type,
// }) {
// return News(
// sophoraId: sophoraId ?? this.sophoraId,
// externalId: externalId ?? this.externalId,
// title: title ?? this.title,
// teaserImage: teaserImage ?? this.teaserImage,
// brandingImage: brandingImage ?? this.brandingImage,
// date: date ?? this.date,
// updateCheckUrl: updateCheckUrl ?? this.updateCheckUrl,
// regionId: regionId ?? this.regionId,
// details: details ?? this.details,
// detailsweb: detailsweb ?? this.detailsweb,
// shareURL: shareURL ?? this.shareURL,
// topline: topline ?? this.topline,
// firstSentence: firstSentence ?? this.firstSentence,
// type: type ?? this.type,
// );
// }
// Map<String, dynamic> toMap() {
// return {
// 'sophoraId': sophoraId,
// 'externalId': externalId,
// 'title': title,
// 'teaserImage': teaserImage?.toMap(),
// 'brandingImage': brandingImage?.toMap(),
// 'date': date,
// 'updateCheckUrl': updateCheckUrl,
// 'regionId': regionId,
// 'details': details,
// 'detailsweb': detailsweb,
// 'shareURL': shareURL,
// 'topline': topline,
// 'firstSentence': firstSentence,
// 'type': type,
// };
// }
// factory News.fromMap(Map<String, dynamic> map) {
// return News(
// sophoraId: map['sophoraId'] ?? '',
// externalId: map['externalId'] ?? '',
// title: map['title'] ?? '',
// teaserImage: map['teaserImage'] != null
// ? TeaserImage.fromMap(map['teaserImage'])
// : null,
// brandingImage: map['brandingImage'] != null
// ? TeaserImage.fromMap(map['brandingImage'])
// : null,
// date: map['date'] ?? '',
// updateCheckUrl: map['updateCheckUrl'] ?? '',
// regionId: map['regionId']?.toInt() ?? 0,
// details: map['details'] ?? '',
// detailsweb: map['detailsweb'] ?? '',
// shareURL: map['shareURL'] ?? '',
// topline: map['topline'] ?? '',
// firstSentence: map['firstSentence'] ?? '',
// type: map['type'] ?? '',
// );
// }
// String toJson() => json.encode(toMap());
// factory News.fromJson(String source) => News.fromMap(json.decode(source));
// }

View File

@ -0,0 +1,273 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'news.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
News _$NewsFromJson(Map<String, dynamic> json) {
return _News.fromJson(json);
}
/// @nodoc
mixin _$News {
String? get title => throw _privateConstructorUsedError;
TeaserImage? get teaserImage => throw _privateConstructorUsedError;
TeaserImage? get brandingImage => throw _privateConstructorUsedError;
String? get date => throw _privateConstructorUsedError;
String? get type => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$NewsCopyWith<News> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $NewsCopyWith<$Res> {
factory $NewsCopyWith(News value, $Res Function(News) then) =
_$NewsCopyWithImpl<$Res, News>;
@useResult
$Res call(
{String? title,
TeaserImage? teaserImage,
TeaserImage? brandingImage,
String? date,
String? type});
$TeaserImageCopyWith<$Res>? get teaserImage;
$TeaserImageCopyWith<$Res>? get brandingImage;
}
/// @nodoc
class _$NewsCopyWithImpl<$Res, $Val extends News>
implements $NewsCopyWith<$Res> {
_$NewsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? title = freezed,
Object? teaserImage = freezed,
Object? brandingImage = freezed,
Object? date = freezed,
Object? type = freezed,
}) {
return _then(_value.copyWith(
title: freezed == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String?,
teaserImage: freezed == teaserImage
? _value.teaserImage
: teaserImage // ignore: cast_nullable_to_non_nullable
as TeaserImage?,
brandingImage: freezed == brandingImage
? _value.brandingImage
: brandingImage // ignore: cast_nullable_to_non_nullable
as TeaserImage?,
date: freezed == date
? _value.date
: date // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
@override
@pragma('vm:prefer-inline')
$TeaserImageCopyWith<$Res>? get teaserImage {
if (_value.teaserImage == null) {
return null;
}
return $TeaserImageCopyWith<$Res>(_value.teaserImage!, (value) {
return _then(_value.copyWith(teaserImage: value) as $Val);
});
}
@override
@pragma('vm:prefer-inline')
$TeaserImageCopyWith<$Res>? get brandingImage {
if (_value.brandingImage == null) {
return null;
}
return $TeaserImageCopyWith<$Res>(_value.brandingImage!, (value) {
return _then(_value.copyWith(brandingImage: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$NewsImplCopyWith<$Res> implements $NewsCopyWith<$Res> {
factory _$$NewsImplCopyWith(
_$NewsImpl value, $Res Function(_$NewsImpl) then) =
__$$NewsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String? title,
TeaserImage? teaserImage,
TeaserImage? brandingImage,
String? date,
String? type});
@override
$TeaserImageCopyWith<$Res>? get teaserImage;
@override
$TeaserImageCopyWith<$Res>? get brandingImage;
}
/// @nodoc
class __$$NewsImplCopyWithImpl<$Res>
extends _$NewsCopyWithImpl<$Res, _$NewsImpl>
implements _$$NewsImplCopyWith<$Res> {
__$$NewsImplCopyWithImpl(_$NewsImpl _value, $Res Function(_$NewsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? title = freezed,
Object? teaserImage = freezed,
Object? brandingImage = freezed,
Object? date = freezed,
Object? type = freezed,
}) {
return _then(_$NewsImpl(
title: freezed == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String?,
teaserImage: freezed == teaserImage
? _value.teaserImage
: teaserImage // ignore: cast_nullable_to_non_nullable
as TeaserImage?,
brandingImage: freezed == brandingImage
? _value.brandingImage
: brandingImage // ignore: cast_nullable_to_non_nullable
as TeaserImage?,
date: freezed == date
? _value.date
: date // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$NewsImpl with DiagnosticableTreeMixin implements _News {
const _$NewsImpl(
{this.title, this.teaserImage, this.brandingImage, this.date, this.type});
factory _$NewsImpl.fromJson(Map<String, dynamic> json) =>
_$$NewsImplFromJson(json);
@override
final String? title;
@override
final TeaserImage? teaserImage;
@override
final TeaserImage? brandingImage;
@override
final String? date;
@override
final String? type;
@override
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
return 'News(title: $title, teaserImage: $teaserImage, brandingImage: $brandingImage, date: $date, type: $type)';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('type', 'News'))
..add(DiagnosticsProperty('title', title))
..add(DiagnosticsProperty('teaserImage', teaserImage))
..add(DiagnosticsProperty('brandingImage', brandingImage))
..add(DiagnosticsProperty('date', date))
..add(DiagnosticsProperty('type', type));
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$NewsImpl &&
(identical(other.title, title) || other.title == title) &&
(identical(other.teaserImage, teaserImage) ||
other.teaserImage == teaserImage) &&
(identical(other.brandingImage, brandingImage) ||
other.brandingImage == brandingImage) &&
(identical(other.date, date) || other.date == date) &&
(identical(other.type, type) || other.type == type));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, title, teaserImage, brandingImage, date, type);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$NewsImplCopyWith<_$NewsImpl> get copyWith =>
__$$NewsImplCopyWithImpl<_$NewsImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$NewsImplToJson(
this,
);
}
}
abstract class _News implements News {
const factory _News(
{final String? title,
final TeaserImage? teaserImage,
final TeaserImage? brandingImage,
final String? date,
final String? type}) = _$NewsImpl;
factory _News.fromJson(Map<String, dynamic> json) = _$NewsImpl.fromJson;
@override
String? get title;
@override
TeaserImage? get teaserImage;
@override
TeaserImage? get brandingImage;
@override
String? get date;
@override
String? get type;
@override
@JsonKey(ignore: true)
_$$NewsImplCopyWith<_$NewsImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,28 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'news.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$NewsImpl _$$NewsImplFromJson(Map<String, dynamic> json) => _$NewsImpl(
title: json['title'] as String?,
teaserImage: json['teaserImage'] == null
? null
: TeaserImage.fromJson(json['teaserImage'] as Map<String, dynamic>),
brandingImage: json['brandingImage'] == null
? null
: TeaserImage.fromJson(json['brandingImage'] as Map<String, dynamic>),
date: json['date'] as String?,
type: json['type'] as String?,
);
Map<String, dynamic> _$$NewsImplToJson(_$NewsImpl instance) =>
<String, dynamic>{
'title': instance.title,
'teaserImage': instance.teaserImage,
'brandingImage': instance.brandingImage,
'date': instance.date,
'type': instance.type,
};

View File

@ -0,0 +1,94 @@
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'news.dart';
part 'region_news.freezed.dart';
part 'region_news.g.dart';
@freezed
class RegionNews with _$RegionNews {
const factory RegionNews({
required List<News> news,
String? nextPage,
}) = _RegionNews;
factory RegionNews.fromJson(Map<String, dynamic> json) =>
_$RegionNewsFromJson(json);
}
// old manual implementation:
// class RegionNews {
// List<News> news;
// String newStoriesCountLink;
// String type;
// String? nextPage;
// RegionNews({
// required this.news,
// required this.newStoriesCountLink,
// required this.type,
// this.nextPage,
// });
// RegionNews copyWith({
// List<News>? news,
// String? newStoriesCountLink,
// String? type,
// String? nextPage,
// }) {
// return RegionNews(
// news: news ?? this.news,
// newStoriesCountLink: newStoriesCountLink ?? this.newStoriesCountLink,
// type: type ?? this.type,
// nextPage: nextPage ?? this.nextPage,
// );
// }
// Map<String, dynamic> toMap() {
// return {
// 'news': news.map((x) => x.toMap()).toList(),
// 'newStoriesCountLink': newStoriesCountLink,
// 'type': type,
// 'nextPage': nextPage,
// };
// }
// factory RegionNews.fromMap(Map<String, dynamic> map) {
// return RegionNews(
// news: List<News>.from(map['news']?.map((x) => News.fromMap(x))),
// newStoriesCountLink: map['newStoriesCountLink'] ?? '',
// type: map['type'] ?? '',
// nextPage: map['nextPage'],
// );
// }
// String toJson() => json.encode(toMap());
// factory RegionNews.fromJson(String source) =>
// RegionNews.fromMap(json.decode(source));
// @override
// String toString() {
// return 'RegionNews(news: $news, newStoriesCountLink: $newStoriesCountLink, type: $type, nextPage: $nextPage)';
// }
// @override
// bool operator ==(Object other) {
// if (identical(this, other)) return true;
// return other is RegionNews &&
// listEquals(other.news, news) &&
// other.newStoriesCountLink == newStoriesCountLink &&
// other.type == type &&
// other.nextPage == nextPage;
// }
// @override
// int get hashCode {
// return news.hashCode ^
// newStoriesCountLink.hashCode ^
// type.hashCode ^
// nextPage.hashCode;
// }
// }

View File

@ -0,0 +1,187 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'region_news.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
RegionNews _$RegionNewsFromJson(Map<String, dynamic> json) {
return _RegionNews.fromJson(json);
}
/// @nodoc
mixin _$RegionNews {
List<News> get news => throw _privateConstructorUsedError;
String? get nextPage => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$RegionNewsCopyWith<RegionNews> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $RegionNewsCopyWith<$Res> {
factory $RegionNewsCopyWith(
RegionNews value, $Res Function(RegionNews) then) =
_$RegionNewsCopyWithImpl<$Res, RegionNews>;
@useResult
$Res call({List<News> news, String? nextPage});
}
/// @nodoc
class _$RegionNewsCopyWithImpl<$Res, $Val extends RegionNews>
implements $RegionNewsCopyWith<$Res> {
_$RegionNewsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? news = null,
Object? nextPage = freezed,
}) {
return _then(_value.copyWith(
news: null == news
? _value.news
: news // ignore: cast_nullable_to_non_nullable
as List<News>,
nextPage: freezed == nextPage
? _value.nextPage
: nextPage // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
}
/// @nodoc
abstract class _$$RegionNewsImplCopyWith<$Res>
implements $RegionNewsCopyWith<$Res> {
factory _$$RegionNewsImplCopyWith(
_$RegionNewsImpl value, $Res Function(_$RegionNewsImpl) then) =
__$$RegionNewsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<News> news, String? nextPage});
}
/// @nodoc
class __$$RegionNewsImplCopyWithImpl<$Res>
extends _$RegionNewsCopyWithImpl<$Res, _$RegionNewsImpl>
implements _$$RegionNewsImplCopyWith<$Res> {
__$$RegionNewsImplCopyWithImpl(
_$RegionNewsImpl _value, $Res Function(_$RegionNewsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? news = null,
Object? nextPage = freezed,
}) {
return _then(_$RegionNewsImpl(
news: null == news
? _value._news
: news // ignore: cast_nullable_to_non_nullable
as List<News>,
nextPage: freezed == nextPage
? _value.nextPage
: nextPage // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$RegionNewsImpl with DiagnosticableTreeMixin implements _RegionNews {
const _$RegionNewsImpl({required final List<News> news, this.nextPage})
: _news = news;
factory _$RegionNewsImpl.fromJson(Map<String, dynamic> json) =>
_$$RegionNewsImplFromJson(json);
final List<News> _news;
@override
List<News> get news {
if (_news is EqualUnmodifiableListView) return _news;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_news);
}
@override
final String? nextPage;
@override
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
return 'RegionNews(news: $news, nextPage: $nextPage)';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('type', 'RegionNews'))
..add(DiagnosticsProperty('news', news))
..add(DiagnosticsProperty('nextPage', nextPage));
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RegionNewsImpl &&
const DeepCollectionEquality().equals(other._news, _news) &&
(identical(other.nextPage, nextPage) ||
other.nextPage == nextPage));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(_news), nextPage);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$RegionNewsImplCopyWith<_$RegionNewsImpl> get copyWith =>
__$$RegionNewsImplCopyWithImpl<_$RegionNewsImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$RegionNewsImplToJson(
this,
);
}
}
abstract class _RegionNews implements RegionNews {
const factory _RegionNews(
{required final List<News> news,
final String? nextPage}) = _$RegionNewsImpl;
factory _RegionNews.fromJson(Map<String, dynamic> json) =
_$RegionNewsImpl.fromJson;
@override
List<News> get news;
@override
String? get nextPage;
@override
@JsonKey(ignore: true)
_$$RegionNewsImplCopyWith<_$RegionNewsImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'region_news.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$RegionNewsImpl _$$RegionNewsImplFromJson(Map<String, dynamic> json) =>
_$RegionNewsImpl(
news: (json['news'] as List<dynamic>)
.map((e) => News.fromJson(e as Map<String, dynamic>))
.toList(),
nextPage: json['nextPage'] as String?,
);
Map<String, dynamic> _$$RegionNewsImplToJson(_$RegionNewsImpl instance) =>
<String, dynamic>{
'news': instance.news,
'nextPage': instance.nextPage,
};

View File

@ -0,0 +1,62 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'teaser_image.freezed.dart';
part 'teaser_image.g.dart';
@freezed
class TeaserImage with _$TeaserImage {
const factory TeaserImage({
Map<String, String>? imageVariants,
}) = _TeaserImage;
factory TeaserImage.fromJson(Map<String, dynamic> json) =>
_$TeaserImageFromJson(json);
}
// old manual implementation:
// class TeaserImage {
// final String? title;
// final String alttext;
// final String? copyright;
// final String type;
// final Map<String, String> imageVariants;
// TeaserImage({
// this.title,
// required this.alttext,
// this.copyright,
// required this.type,
// required this.imageVariants,
// });
// Map<String, dynamic> toMap() {
// return {
// 'title': title,
// 'alttext': alttext,
// 'copyright': copyright,
// 'type': type,
// 'imageVariants': Map.from(imageVariants),
// };
// }
// factory TeaserImage.fromMap(Map<String, dynamic> map) {
// return TeaserImage(
// title: map['title'] ?? '',
// alttext: map['alttext'] ?? '',
// copyright: map['copyright'],
// type: map['type'] ?? '',
// imageVariants: Map.from(map['imageVariants']),
// );
// }
// String toJson() => json.encode(toMap());
// factory TeaserImage.fromJson(String source) =>
// TeaserImage.fromMap(json.decode(source));
// @override
// String toString() {
// return 'TeaserImage(title: $title, alttext: $alttext, copyright: $copyright, type: $type)';
// }
// }

View File

@ -0,0 +1,162 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'teaser_image.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
TeaserImage _$TeaserImageFromJson(Map<String, dynamic> json) {
return _TeaserImage.fromJson(json);
}
/// @nodoc
mixin _$TeaserImage {
Map<String, String>? get imageVariants => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$TeaserImageCopyWith<TeaserImage> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TeaserImageCopyWith<$Res> {
factory $TeaserImageCopyWith(
TeaserImage value, $Res Function(TeaserImage) then) =
_$TeaserImageCopyWithImpl<$Res, TeaserImage>;
@useResult
$Res call({Map<String, String>? imageVariants});
}
/// @nodoc
class _$TeaserImageCopyWithImpl<$Res, $Val extends TeaserImage>
implements $TeaserImageCopyWith<$Res> {
_$TeaserImageCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? imageVariants = freezed,
}) {
return _then(_value.copyWith(
imageVariants: freezed == imageVariants
? _value.imageVariants
: imageVariants // ignore: cast_nullable_to_non_nullable
as Map<String, String>?,
) as $Val);
}
}
/// @nodoc
abstract class _$$TeaserImageImplCopyWith<$Res>
implements $TeaserImageCopyWith<$Res> {
factory _$$TeaserImageImplCopyWith(
_$TeaserImageImpl value, $Res Function(_$TeaserImageImpl) then) =
__$$TeaserImageImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({Map<String, String>? imageVariants});
}
/// @nodoc
class __$$TeaserImageImplCopyWithImpl<$Res>
extends _$TeaserImageCopyWithImpl<$Res, _$TeaserImageImpl>
implements _$$TeaserImageImplCopyWith<$Res> {
__$$TeaserImageImplCopyWithImpl(
_$TeaserImageImpl _value, $Res Function(_$TeaserImageImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? imageVariants = freezed,
}) {
return _then(_$TeaserImageImpl(
imageVariants: freezed == imageVariants
? _value._imageVariants
: imageVariants // ignore: cast_nullable_to_non_nullable
as Map<String, String>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$TeaserImageImpl implements _TeaserImage {
const _$TeaserImageImpl({final Map<String, String>? imageVariants})
: _imageVariants = imageVariants;
factory _$TeaserImageImpl.fromJson(Map<String, dynamic> json) =>
_$$TeaserImageImplFromJson(json);
final Map<String, String>? _imageVariants;
@override
Map<String, String>? get imageVariants {
final value = _imageVariants;
if (value == null) return null;
if (_imageVariants is EqualUnmodifiableMapView) return _imageVariants;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(value);
}
@override
String toString() {
return 'TeaserImage(imageVariants: $imageVariants)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TeaserImageImpl &&
const DeepCollectionEquality()
.equals(other._imageVariants, _imageVariants));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(_imageVariants));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$TeaserImageImplCopyWith<_$TeaserImageImpl> get copyWith =>
__$$TeaserImageImplCopyWithImpl<_$TeaserImageImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$TeaserImageImplToJson(
this,
);
}
}
abstract class _TeaserImage implements TeaserImage {
const factory _TeaserImage({final Map<String, String>? imageVariants}) =
_$TeaserImageImpl;
factory _TeaserImage.fromJson(Map<String, dynamic> json) =
_$TeaserImageImpl.fromJson;
@override
Map<String, String>? get imageVariants;
@override
@JsonKey(ignore: true)
_$$TeaserImageImplCopyWith<_$TeaserImageImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'teaser_image.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$TeaserImageImpl _$$TeaserImageImplFromJson(Map<String, dynamic> json) =>
_$TeaserImageImpl(
imageVariants: (json['imageVariants'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
),
);
Map<String, dynamic> _$$TeaserImageImplToJson(_$TeaserImageImpl instance) =>
<String, dynamic>{
'imageVariants': instance.imageVariants,
};

View File

@ -0,0 +1,62 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'teaser_image_url.freezed.dart';
part 'teaser_image_url.g.dart';
@freezed
class TeaserImageUrl with _$TeaserImageUrl {
const factory TeaserImageUrl({
String? imageurl,
}) = _TeaserImageUrl;
factory TeaserImageUrl.fromJson(Map<String, dynamic> json) =>
_$TeaserImageUrlFromJson(json);
}
// old manual implementation:
// class TeaserImageUrl {
// final String imageurl;
// TeaserImageUrl({
// required this.imageurl,
// });
// TeaserImageUrl copyWith({
// String? imageurl,
// }) {
// return TeaserImageUrl(
// imageurl: imageurl ?? this.imageurl,
// );
// }
// Map<String, dynamic> toMap() {
// return {
// 'imageurl': imageurl,
// };
// }
// factory TeaserImageUrl.fromMap(Map<String, dynamic> map) {
// return TeaserImageUrl(
// imageurl: map['imageurl'] ?? '',
// );
// }
// String toJson() => json.encode(toMap());
// factory TeaserImageUrl.fromJson(String source) =>
// TeaserImageUrl.fromMap(json.decode(source));
// @override
// String toString() => 'TeaserImageUrl(imageurl: $imageurl)';
// @override
// bool operator ==(Object other) {
// if (identical(this, other)) return true;
// return other is TeaserImageUrl && other.imageurl == imageurl;
// }
// @override
// int get hashCode => imageurl.hashCode;
// }

View File

@ -0,0 +1,154 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'teaser_image_url.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
TeaserImageUrl _$TeaserImageUrlFromJson(Map<String, dynamic> json) {
return _TeaserImageUrl.fromJson(json);
}
/// @nodoc
mixin _$TeaserImageUrl {
String? get imageurl => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$TeaserImageUrlCopyWith<TeaserImageUrl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TeaserImageUrlCopyWith<$Res> {
factory $TeaserImageUrlCopyWith(
TeaserImageUrl value, $Res Function(TeaserImageUrl) then) =
_$TeaserImageUrlCopyWithImpl<$Res, TeaserImageUrl>;
@useResult
$Res call({String? imageurl});
}
/// @nodoc
class _$TeaserImageUrlCopyWithImpl<$Res, $Val extends TeaserImageUrl>
implements $TeaserImageUrlCopyWith<$Res> {
_$TeaserImageUrlCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? imageurl = freezed,
}) {
return _then(_value.copyWith(
imageurl: freezed == imageurl
? _value.imageurl
: imageurl // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
}
/// @nodoc
abstract class _$$TeaserImageUrlImplCopyWith<$Res>
implements $TeaserImageUrlCopyWith<$Res> {
factory _$$TeaserImageUrlImplCopyWith(_$TeaserImageUrlImpl value,
$Res Function(_$TeaserImageUrlImpl) then) =
__$$TeaserImageUrlImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String? imageurl});
}
/// @nodoc
class __$$TeaserImageUrlImplCopyWithImpl<$Res>
extends _$TeaserImageUrlCopyWithImpl<$Res, _$TeaserImageUrlImpl>
implements _$$TeaserImageUrlImplCopyWith<$Res> {
__$$TeaserImageUrlImplCopyWithImpl(
_$TeaserImageUrlImpl _value, $Res Function(_$TeaserImageUrlImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? imageurl = freezed,
}) {
return _then(_$TeaserImageUrlImpl(
imageurl: freezed == imageurl
? _value.imageurl
: imageurl // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$TeaserImageUrlImpl implements _TeaserImageUrl {
const _$TeaserImageUrlImpl({this.imageurl});
factory _$TeaserImageUrlImpl.fromJson(Map<String, dynamic> json) =>
_$$TeaserImageUrlImplFromJson(json);
@override
final String? imageurl;
@override
String toString() {
return 'TeaserImageUrl(imageurl: $imageurl)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TeaserImageUrlImpl &&
(identical(other.imageurl, imageurl) ||
other.imageurl == imageurl));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(runtimeType, imageurl);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$TeaserImageUrlImplCopyWith<_$TeaserImageUrlImpl> get copyWith =>
__$$TeaserImageUrlImplCopyWithImpl<_$TeaserImageUrlImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$TeaserImageUrlImplToJson(
this,
);
}
}
abstract class _TeaserImageUrl implements TeaserImageUrl {
const factory _TeaserImageUrl({final String? imageurl}) =
_$TeaserImageUrlImpl;
factory _TeaserImageUrl.fromJson(Map<String, dynamic> json) =
_$TeaserImageUrlImpl.fromJson;
@override
String? get imageurl;
@override
@JsonKey(ignore: true)
_$$TeaserImageUrlImplCopyWith<_$TeaserImageUrlImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,18 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'teaser_image_url.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$TeaserImageUrlImpl _$$TeaserImageUrlImplFromJson(Map<String, dynamic> json) =>
_$TeaserImageUrlImpl(
imageurl: json['imageurl'] as String?,
);
Map<String, dynamic> _$$TeaserImageUrlImplToJson(
_$TeaserImageUrlImpl instance) =>
<String, dynamic>{
'imageurl': instance.imageurl,
};

Some files were not shown because too many files have changed in this diff Show More