initial commit

This commit is contained in:
2025-10-16 14:50:56 +02:00
commit af9d2d58e8
140 changed files with 6419 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/api_client.dart';
final apiClientProvider = Provider<ApiClient>((ref) => ApiClient());
class AuthState {
final bool isAuthenticated;
final String? email;
const AuthState({required this.isAuthenticated, this.email});
}
final authProvider =
NotifierProvider<AuthController, AuthState>(() => AuthController());
class AuthController extends Notifier<AuthState> {
late final ApiClient _api = ref.read(apiClientProvider);
@override
AuthState build() => const AuthState(isAuthenticated: false);
Future<void> login(String email, String password) async {
await _api.call('login', {'email': email, 'password': password});
state = AuthState(isAuthenticated: true, email: email);
}
Future<void> logout() async {
await _api.call('logout', {});
state = const AuthState(isAuthenticated: false);
}
Future<void> register(String email, String password) async {
await _api.call('register', {'email': email, 'password': password});
}
Future<void> checkSession() async {
try {
final data = await _api.call('session', {});
if (data['loggedIn'] == true) {
state = AuthState(isAuthenticated: true, email: data['email']);
}
} catch (_) {
state = const AuthState(isAuthenticated: false);
}
}
}
+117
View File
@@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dio/dio.dart'; // <— wichtig für DioException
import '../auth_controller.dart';
import 'package:go_router/go_router.dart';
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({super.key});
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen> {
final emailCtrl = TextEditingController();
final passCtrl = TextEditingController();
bool loading = false;
String? error;
Future<void> _submit() async {
setState(() => loading = true);
try {
await ref
.read(authProvider.notifier)
.login(emailCtrl.text, passCtrl.text);
if (mounted) context.go('/');
} catch (e) {
setState(() => error = e.toString());
} finally {
setState(() => loading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Igel Login',
style:
TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
TextField(
controller: emailCtrl,
decoration: const InputDecoration(labelText: 'E-Mail')),
TextField(
controller: passCtrl,
decoration: const InputDecoration(labelText: 'Passwort'),
obscureText: true),
const SizedBox(height: 16),
if (error != null)
Text(error!, style: const TextStyle(color: Colors.red)),
const SizedBox(height: 8),
ElevatedButton(
onPressed: loading ? null : _submit,
child: loading
? const CircularProgressIndicator()
: const Text('Login'),
),
TextButton(
onPressed: () async {
final email = emailCtrl.text.trim();
final pw = passCtrl.text;
if (email.isEmpty ||
!email.contains('@') ||
pw.length < 6) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Bitte gültige E-Mail und Passwort (min. 6 Zeichen) eingeben.')),
);
return;
}
try {
await ref
.read(authProvider.notifier)
.register(email, pw);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Registrierung erfolgreich bitte einloggen.')),
);
} on DioException catch (e) {
final msg = e.response?.data is Map &&
(e.response!.data['error'] != null)
? e.response!.data['error'].toString()
: e.message ?? 'Unbekannter Fehler';
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Fehler: $msg')),
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Fehler: $e')),
);
}
},
child: const Text('Neu registrieren'),
),
],
),
),
),
),
),
);
}
}
@@ -0,0 +1,24 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/api_client.dart';
import '../models/hedgehog.dart';
class HedgehogService {
final Ref ref;
HedgehogService(this.ref);
ApiClient get _api => ref.read(apiClientProvider);
Future<List<Hedgehog>> list() async {
final data = await _api.call('hedgehog.list', {});
return (data as List)
.map((e) => Hedgehog.fromJson(Map<String, dynamic>.from(e)))
.toList();
}
Future<Hedgehog> create(String name) async {
final data = await _api.call('hedgehog.create', {'name': name});
return Hedgehog.fromJson(Map<String, dynamic>.from(data));
}
}
final hedgehogServiceProvider = Provider((ref) => HedgehogService(ref));
@@ -0,0 +1,16 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'hedgehog.freezed.dart';
part 'hedgehog.g.dart';
@freezed
class Hedgehog with _$Hedgehog {
const factory Hedgehog({
required int id,
required String name,
String? species,
String? notes,
}) = _Hedgehog;
factory Hedgehog.fromJson(Map<String, dynamic> json) =>
_$HedgehogFromJson(json);
}
@@ -0,0 +1,216 @@
// 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 'hedgehog.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');
Hedgehog _$HedgehogFromJson(Map<String, dynamic> json) {
return _Hedgehog.fromJson(json);
}
/// @nodoc
mixin _$Hedgehog {
int get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String? get species => throw _privateConstructorUsedError;
String? get notes => throw _privateConstructorUsedError;
/// Serializes this Hedgehog to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Hedgehog
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$HedgehogCopyWith<Hedgehog> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $HedgehogCopyWith<$Res> {
factory $HedgehogCopyWith(Hedgehog value, $Res Function(Hedgehog) then) =
_$HedgehogCopyWithImpl<$Res, Hedgehog>;
@useResult
$Res call({int id, String name, String? species, String? notes});
}
/// @nodoc
class _$HedgehogCopyWithImpl<$Res, $Val extends Hedgehog>
implements $HedgehogCopyWith<$Res> {
_$HedgehogCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Hedgehog
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? name = null,
Object? species = freezed,
Object? notes = freezed,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
species: freezed == species
? _value.species
: species // ignore: cast_nullable_to_non_nullable
as String?,
notes: freezed == notes
? _value.notes
: notes // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
}
/// @nodoc
abstract class _$$HedgehogImplCopyWith<$Res>
implements $HedgehogCopyWith<$Res> {
factory _$$HedgehogImplCopyWith(
_$HedgehogImpl value, $Res Function(_$HedgehogImpl) then) =
__$$HedgehogImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int id, String name, String? species, String? notes});
}
/// @nodoc
class __$$HedgehogImplCopyWithImpl<$Res>
extends _$HedgehogCopyWithImpl<$Res, _$HedgehogImpl>
implements _$$HedgehogImplCopyWith<$Res> {
__$$HedgehogImplCopyWithImpl(
_$HedgehogImpl _value, $Res Function(_$HedgehogImpl) _then)
: super(_value, _then);
/// Create a copy of Hedgehog
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? name = null,
Object? species = freezed,
Object? notes = freezed,
}) {
return _then(_$HedgehogImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
species: freezed == species
? _value.species
: species // ignore: cast_nullable_to_non_nullable
as String?,
notes: freezed == notes
? _value.notes
: notes // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$HedgehogImpl implements _Hedgehog {
const _$HedgehogImpl(
{required this.id, required this.name, this.species, this.notes});
factory _$HedgehogImpl.fromJson(Map<String, dynamic> json) =>
_$$HedgehogImplFromJson(json);
@override
final int id;
@override
final String name;
@override
final String? species;
@override
final String? notes;
@override
String toString() {
return 'Hedgehog(id: $id, name: $name, species: $species, notes: $notes)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$HedgehogImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.species, species) || other.species == species) &&
(identical(other.notes, notes) || other.notes == notes));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, name, species, notes);
/// Create a copy of Hedgehog
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$HedgehogImplCopyWith<_$HedgehogImpl> get copyWith =>
__$$HedgehogImplCopyWithImpl<_$HedgehogImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$HedgehogImplToJson(
this,
);
}
}
abstract class _Hedgehog implements Hedgehog {
const factory _Hedgehog(
{required final int id,
required final String name,
final String? species,
final String? notes}) = _$HedgehogImpl;
factory _Hedgehog.fromJson(Map<String, dynamic> json) =
_$HedgehogImpl.fromJson;
@override
int get id;
@override
String get name;
@override
String? get species;
@override
String? get notes;
/// Create a copy of Hedgehog
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$HedgehogImplCopyWith<_$HedgehogImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,23 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'hedgehog.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$HedgehogImpl _$$HedgehogImplFromJson(Map<String, dynamic> json) =>
_$HedgehogImpl(
id: (json['id'] as num).toInt(),
name: json['name'] as String,
species: json['species'] as String?,
notes: json['notes'] as String?,
);
Map<String, dynamic> _$$HedgehogImplToJson(_$HedgehogImpl instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'species': instance.species,
'notes': instance.notes,
};
@@ -0,0 +1,181 @@
// lib/features/hedgehogs/ui/dashboard_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../auth/auth_controller.dart';
import '../data/hedgehog_service.dart';
import '../models/hedgehog.dart';
class DashboardScreen extends ConsumerStatefulWidget {
const DashboardScreen({super.key});
@override
ConsumerState<DashboardScreen> createState() => _DashboardScreenState();
}
class _DashboardScreenState extends ConsumerState<DashboardScreen> {
late Future<List<Hedgehog>> _future;
@override
void initState() {
super.initState();
_future = ref.read(hedgehogServiceProvider).list();
}
Future<void> _reload() async {
setState(() {
_future = ref.read(hedgehogServiceProvider).list();
});
}
Future<void> _createHedgehogDialog() async {
final service = ref.read(hedgehogServiceProvider);
final nameCtrl = TextEditingController();
final created = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Neuer Igel'),
content: TextField(
controller: nameCtrl,
decoration: const InputDecoration(
labelText: 'Name',
hintText: 'z. B. Frida',
),
textInputAction: TextInputAction.done,
onSubmitted: (_) => Navigator.of(ctx).pop(true),
),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(false),
child: const Text('Abbrechen'),
),
FilledButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: const Text('Speichern'),
),
],
),
);
if (created == true && nameCtrl.text.trim().isNotEmpty) {
try {
await service.create(nameCtrl.text.trim());
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Igel angelegt.')),
);
await _reload();
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Fehler beim Anlegen: $e')),
);
}
}
}
@override
Widget build(BuildContext context) {
final auth = ref.watch(authProvider);
return Scaffold(
appBar: AppBar(
title: Text('Meine Igel${auth.email != null ? " (${auth.email})" : ""}'),
actions: [
IconButton(
tooltip: 'Abmelden',
onPressed: () async {
await ref.read(authProvider.notifier).logout();
if (!mounted) return;
context.go('/login');
},
icon: const Icon(Icons.logout),
),
],
),
body: RefreshIndicator(
onRefresh: _reload,
child: FutureBuilder<List<Hedgehog>>(
future: _future,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return ListView(
children: [
const SizedBox(height: 48),
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Fehler beim Laden:\n${snapshot.error}',
textAlign: TextAlign.center,
),
),
),
const SizedBox(height: 12),
Center(
child: FilledButton.icon(
onPressed: _reload,
icon: const Icon(Icons.refresh),
label: const Text('Erneut versuchen'),
),
),
],
);
}
final list = snapshot.data ?? const <Hedgehog>[];
if (list.isEmpty) {
return ListView(
children: [
const SizedBox(height: 80),
const Icon(Icons.pets, size: 72),
const SizedBox(height: 12),
const Center(
child: Text(
'Noch keine Igel angelegt.',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
),
const SizedBox(height: 8),
Center(
child: TextButton.icon(
onPressed: _createHedgehogDialog,
icon: const Icon(Icons.add),
label: const Text('Jetzt ersten Igel anlegen'),
),
),
],
);
}
return ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 8),
itemCount: list.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, i) {
final h = list[i];
return ListTile(
leading: const Icon(Icons.pets),
title: Text(h.name),
subtitle: Text(h.species ?? ''),
trailing: const Icon(Icons.chevron_right),
onTap: () => context.push('/hedgehogs/${h.id}'),
);
},
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: _createHedgehogDialog,
tooltip: 'Neuer Igel',
child: const Icon(Icons.add),
),
);
}
}
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class HedgehogDetailScreen extends StatelessWidget {
final int id;
const HedgehogDetailScreen({super.key, required this.id});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Igel #$id')),
body: const Center(child: Text('Detailansicht (Chart, Messungen etc.) kommt später')),
);
}
}
@@ -0,0 +1,19 @@
// lib/features/measurements/models/measurement.dart
import 'package:freezed_annotation/freezed_annotation.dart';
part 'measurement.freezed.dart';
part 'measurement.g.dart';
@freezed
class Measurement with _$Measurement {
const factory Measurement({
required int id,
required int hedgehogId,
required DateTime measuredAt,
int? weightGrams,
int? lengthMm,
double? temperatureC,
String? note,
}) = _Measurement;
factory Measurement.fromJson(Map<String, dynamic> json) => _$MeasurementFromJson(json);
}
@@ -0,0 +1,294 @@
// 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 'measurement.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');
Measurement _$MeasurementFromJson(Map<String, dynamic> json) {
return _Measurement.fromJson(json);
}
/// @nodoc
mixin _$Measurement {
int get id => throw _privateConstructorUsedError;
int get hedgehogId => throw _privateConstructorUsedError;
DateTime get measuredAt => throw _privateConstructorUsedError;
int? get weightGrams => throw _privateConstructorUsedError;
int? get lengthMm => throw _privateConstructorUsedError;
double? get temperatureC => throw _privateConstructorUsedError;
String? get note => throw _privateConstructorUsedError;
/// Serializes this Measurement to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Measurement
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$MeasurementCopyWith<Measurement> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $MeasurementCopyWith<$Res> {
factory $MeasurementCopyWith(
Measurement value, $Res Function(Measurement) then) =
_$MeasurementCopyWithImpl<$Res, Measurement>;
@useResult
$Res call(
{int id,
int hedgehogId,
DateTime measuredAt,
int? weightGrams,
int? lengthMm,
double? temperatureC,
String? note});
}
/// @nodoc
class _$MeasurementCopyWithImpl<$Res, $Val extends Measurement>
implements $MeasurementCopyWith<$Res> {
_$MeasurementCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Measurement
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? hedgehogId = null,
Object? measuredAt = null,
Object? weightGrams = freezed,
Object? lengthMm = freezed,
Object? temperatureC = freezed,
Object? note = freezed,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
hedgehogId: null == hedgehogId
? _value.hedgehogId
: hedgehogId // ignore: cast_nullable_to_non_nullable
as int,
measuredAt: null == measuredAt
? _value.measuredAt
: measuredAt // ignore: cast_nullable_to_non_nullable
as DateTime,
weightGrams: freezed == weightGrams
? _value.weightGrams
: weightGrams // ignore: cast_nullable_to_non_nullable
as int?,
lengthMm: freezed == lengthMm
? _value.lengthMm
: lengthMm // ignore: cast_nullable_to_non_nullable
as int?,
temperatureC: freezed == temperatureC
? _value.temperatureC
: temperatureC // ignore: cast_nullable_to_non_nullable
as double?,
note: freezed == note
? _value.note
: note // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
}
/// @nodoc
abstract class _$$MeasurementImplCopyWith<$Res>
implements $MeasurementCopyWith<$Res> {
factory _$$MeasurementImplCopyWith(
_$MeasurementImpl value, $Res Function(_$MeasurementImpl) then) =
__$$MeasurementImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int id,
int hedgehogId,
DateTime measuredAt,
int? weightGrams,
int? lengthMm,
double? temperatureC,
String? note});
}
/// @nodoc
class __$$MeasurementImplCopyWithImpl<$Res>
extends _$MeasurementCopyWithImpl<$Res, _$MeasurementImpl>
implements _$$MeasurementImplCopyWith<$Res> {
__$$MeasurementImplCopyWithImpl(
_$MeasurementImpl _value, $Res Function(_$MeasurementImpl) _then)
: super(_value, _then);
/// Create a copy of Measurement
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? hedgehogId = null,
Object? measuredAt = null,
Object? weightGrams = freezed,
Object? lengthMm = freezed,
Object? temperatureC = freezed,
Object? note = freezed,
}) {
return _then(_$MeasurementImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
hedgehogId: null == hedgehogId
? _value.hedgehogId
: hedgehogId // ignore: cast_nullable_to_non_nullable
as int,
measuredAt: null == measuredAt
? _value.measuredAt
: measuredAt // ignore: cast_nullable_to_non_nullable
as DateTime,
weightGrams: freezed == weightGrams
? _value.weightGrams
: weightGrams // ignore: cast_nullable_to_non_nullable
as int?,
lengthMm: freezed == lengthMm
? _value.lengthMm
: lengthMm // ignore: cast_nullable_to_non_nullable
as int?,
temperatureC: freezed == temperatureC
? _value.temperatureC
: temperatureC // ignore: cast_nullable_to_non_nullable
as double?,
note: freezed == note
? _value.note
: note // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$MeasurementImpl implements _Measurement {
const _$MeasurementImpl(
{required this.id,
required this.hedgehogId,
required this.measuredAt,
this.weightGrams,
this.lengthMm,
this.temperatureC,
this.note});
factory _$MeasurementImpl.fromJson(Map<String, dynamic> json) =>
_$$MeasurementImplFromJson(json);
@override
final int id;
@override
final int hedgehogId;
@override
final DateTime measuredAt;
@override
final int? weightGrams;
@override
final int? lengthMm;
@override
final double? temperatureC;
@override
final String? note;
@override
String toString() {
return 'Measurement(id: $id, hedgehogId: $hedgehogId, measuredAt: $measuredAt, weightGrams: $weightGrams, lengthMm: $lengthMm, temperatureC: $temperatureC, note: $note)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MeasurementImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.hedgehogId, hedgehogId) ||
other.hedgehogId == hedgehogId) &&
(identical(other.measuredAt, measuredAt) ||
other.measuredAt == measuredAt) &&
(identical(other.weightGrams, weightGrams) ||
other.weightGrams == weightGrams) &&
(identical(other.lengthMm, lengthMm) ||
other.lengthMm == lengthMm) &&
(identical(other.temperatureC, temperatureC) ||
other.temperatureC == temperatureC) &&
(identical(other.note, note) || other.note == note));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, hedgehogId, measuredAt,
weightGrams, lengthMm, temperatureC, note);
/// Create a copy of Measurement
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$MeasurementImplCopyWith<_$MeasurementImpl> get copyWith =>
__$$MeasurementImplCopyWithImpl<_$MeasurementImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$MeasurementImplToJson(
this,
);
}
}
abstract class _Measurement implements Measurement {
const factory _Measurement(
{required final int id,
required final int hedgehogId,
required final DateTime measuredAt,
final int? weightGrams,
final int? lengthMm,
final double? temperatureC,
final String? note}) = _$MeasurementImpl;
factory _Measurement.fromJson(Map<String, dynamic> json) =
_$MeasurementImpl.fromJson;
@override
int get id;
@override
int get hedgehogId;
@override
DateTime get measuredAt;
@override
int? get weightGrams;
@override
int? get lengthMm;
@override
double? get temperatureC;
@override
String? get note;
/// Create a copy of Measurement
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$MeasurementImplCopyWith<_$MeasurementImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'measurement.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$MeasurementImpl _$$MeasurementImplFromJson(Map<String, dynamic> json) =>
_$MeasurementImpl(
id: (json['id'] as num).toInt(),
hedgehogId: (json['hedgehogId'] as num).toInt(),
measuredAt: DateTime.parse(json['measuredAt'] as String),
weightGrams: (json['weightGrams'] as num?)?.toInt(),
lengthMm: (json['lengthMm'] as num?)?.toInt(),
temperatureC: (json['temperatureC'] as num?)?.toDouble(),
note: json['note'] as String?,
);
Map<String, dynamic> _$$MeasurementImplToJson(_$MeasurementImpl instance) =>
<String, dynamic>{
'id': instance.id,
'hedgehogId': instance.hedgehogId,
'measuredAt': instance.measuredAt.toIso8601String(),
'weightGrams': instance.weightGrams,
'lengthMm': instance.lengthMm,
'temperatureC': instance.temperatureC,
'note': instance.note,
};