diff --git a/lib/api/apis/goal_api.dart b/lib/api/apis/goal_api.dart index 15e2517..682ad18 100644 --- a/lib/api/apis/goal_api.dart +++ b/lib/api/apis/goal_api.dart @@ -7,8 +7,8 @@ class GoalApi { GoalApi({final AppDatabase? db}) : _db = db ?? AppDatabase.instance; - Future> getGoals() { - return _db.select(_db.goal).get(); + Stream> getGoals() { + return _db.select(_db.goal).watch(); } Future getGoal({required final int id}) { @@ -17,12 +17,16 @@ class GoalApi { Future createGoal( {required final String name, + required final double amount, + required final DateTime date, required final double targetAmount, required final DateTime targetDate, required final double inflation, required final GoalImportance importance}) { return _db.into(_db.goal).insert(GoalCompanion.insert( name: name, + amount: amount, + date: date, targetAmount: targetAmount, targetDate: targetDate, inflation: inflation, diff --git a/lib/api/db/app_database.dart b/lib/api/db/app_database.dart index fff4e12..6854ef9 100644 --- a/lib/api/db/app_database.dart +++ b/lib/api/db/app_database.dart @@ -37,8 +37,10 @@ class InvestmentTransaction extends Table { class Goal extends Table { IntColumn get id => integer().named('ID').autoIncrement()(); TextColumn get name => text().named('NAME')(); - RealColumn get targetAmount => real().named('TARGET_AMOUNT')(); + RealColumn get amount => real().named('AMOUNT')(); + DateTimeColumn get date => dateTime().named('DATE')(); RealColumn get inflation => real().named('INFLATION')(); + RealColumn get targetAmount => real().named('TARGET_AMOUNT')(); DateTimeColumn get targetDate => dateTime().named('TARGET_DATE')(); TextColumn get importance => textEnum().named('IMPORTANCE')(); } diff --git a/lib/api/db/app_database.g.dart b/lib/api/db/app_database.g.dart index 71a0a5a..a610cdd 100644 --- a/lib/api/db/app_database.g.dart +++ b/lib/api/db/app_database.g.dart @@ -787,18 +787,28 @@ class $GoalTable extends Goal with TableInfo<$GoalTable, GoalDTO> { late final GeneratedColumn name = GeneratedColumn( 'NAME', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _targetAmountMeta = - const VerificationMeta('targetAmount'); + static const VerificationMeta _amountMeta = const VerificationMeta('amount'); @override - late final GeneratedColumn targetAmount = GeneratedColumn( - 'TARGET_AMOUNT', aliasedName, false, + late final GeneratedColumn amount = GeneratedColumn( + 'AMOUNT', aliasedName, false, type: DriftSqlType.double, requiredDuringInsert: true); + static const VerificationMeta _dateMeta = const VerificationMeta('date'); + @override + late final GeneratedColumn date = GeneratedColumn( + 'DATE', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); static const VerificationMeta _inflationMeta = const VerificationMeta('inflation'); @override late final GeneratedColumn inflation = GeneratedColumn( 'INFLATION', aliasedName, false, type: DriftSqlType.double, requiredDuringInsert: true); + static const VerificationMeta _targetAmountMeta = + const VerificationMeta('targetAmount'); + @override + late final GeneratedColumn targetAmount = GeneratedColumn( + 'TARGET_AMOUNT', aliasedName, false, + type: DriftSqlType.double, requiredDuringInsert: true); static const VerificationMeta _targetDateMeta = const VerificationMeta('targetDate'); @override @@ -814,7 +824,7 @@ class $GoalTable extends Goal with TableInfo<$GoalTable, GoalDTO> { .withConverter($GoalTable.$converterimportance); @override List get $columns => - [id, name, targetAmount, inflation, targetDate, importance]; + [id, name, amount, date, inflation, targetAmount, targetDate, importance]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -834,13 +844,17 @@ class $GoalTable extends Goal with TableInfo<$GoalTable, GoalDTO> { } else if (isInserting) { context.missing(_nameMeta); } - if (data.containsKey('TARGET_AMOUNT')) { + if (data.containsKey('AMOUNT')) { + context.handle(_amountMeta, + amount.isAcceptableOrUnknown(data['AMOUNT']!, _amountMeta)); + } else if (isInserting) { + context.missing(_amountMeta); + } + if (data.containsKey('DATE')) { context.handle( - _targetAmountMeta, - targetAmount.isAcceptableOrUnknown( - data['TARGET_AMOUNT']!, _targetAmountMeta)); + _dateMeta, date.isAcceptableOrUnknown(data['DATE']!, _dateMeta)); } else if (isInserting) { - context.missing(_targetAmountMeta); + context.missing(_dateMeta); } if (data.containsKey('INFLATION')) { context.handle(_inflationMeta, @@ -848,6 +862,14 @@ class $GoalTable extends Goal with TableInfo<$GoalTable, GoalDTO> { } else if (isInserting) { context.missing(_inflationMeta); } + if (data.containsKey('TARGET_AMOUNT')) { + context.handle( + _targetAmountMeta, + targetAmount.isAcceptableOrUnknown( + data['TARGET_AMOUNT']!, _targetAmountMeta)); + } else if (isInserting) { + context.missing(_targetAmountMeta); + } if (data.containsKey('TARGET_DATE')) { context.handle( _targetDateMeta, @@ -870,10 +892,14 @@ class $GoalTable extends Goal with TableInfo<$GoalTable, GoalDTO> { .read(DriftSqlType.int, data['${effectivePrefix}ID'])!, name: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}NAME'])!, - targetAmount: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}TARGET_AMOUNT'])!, + amount: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}AMOUNT'])!, + date: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}DATE'])!, inflation: attachedDatabase.typeMapping .read(DriftSqlType.double, data['${effectivePrefix}INFLATION'])!, + targetAmount: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}TARGET_AMOUNT'])!, targetDate: attachedDatabase.typeMapping .read(DriftSqlType.dateTime, data['${effectivePrefix}TARGET_DATE'])!, importance: $GoalTable.$converterimportance.fromSql(attachedDatabase @@ -895,15 +921,19 @@ class $GoalTable extends Goal with TableInfo<$GoalTable, GoalDTO> { class GoalDTO extends DataClass implements Insertable { final int id; final String name; - final double targetAmount; + final double amount; + final DateTime date; final double inflation; + final double targetAmount; final DateTime targetDate; final GoalImportance importance; const GoalDTO( {required this.id, required this.name, - required this.targetAmount, + required this.amount, + required this.date, required this.inflation, + required this.targetAmount, required this.targetDate, required this.importance}); @override @@ -911,8 +941,10 @@ class GoalDTO extends DataClass implements Insertable { final map = {}; map['ID'] = Variable(id); map['NAME'] = Variable(name); - map['TARGET_AMOUNT'] = Variable(targetAmount); + map['AMOUNT'] = Variable(amount); + map['DATE'] = Variable(date); map['INFLATION'] = Variable(inflation); + map['TARGET_AMOUNT'] = Variable(targetAmount); map['TARGET_DATE'] = Variable(targetDate); { map['IMPORTANCE'] = @@ -925,8 +957,10 @@ class GoalDTO extends DataClass implements Insertable { return GoalCompanion( id: Value(id), name: Value(name), - targetAmount: Value(targetAmount), + amount: Value(amount), + date: Value(date), inflation: Value(inflation), + targetAmount: Value(targetAmount), targetDate: Value(targetDate), importance: Value(importance), ); @@ -938,8 +972,10 @@ class GoalDTO extends DataClass implements Insertable { return GoalDTO( id: serializer.fromJson(json['id']), name: serializer.fromJson(json['name']), - targetAmount: serializer.fromJson(json['targetAmount']), + amount: serializer.fromJson(json['amount']), + date: serializer.fromJson(json['date']), inflation: serializer.fromJson(json['inflation']), + targetAmount: serializer.fromJson(json['targetAmount']), targetDate: serializer.fromJson(json['targetDate']), importance: $GoalTable.$converterimportance .fromJson(serializer.fromJson(json['importance'])), @@ -951,8 +987,10 @@ class GoalDTO extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'name': serializer.toJson(name), - 'targetAmount': serializer.toJson(targetAmount), + 'amount': serializer.toJson(amount), + 'date': serializer.toJson(date), 'inflation': serializer.toJson(inflation), + 'targetAmount': serializer.toJson(targetAmount), 'targetDate': serializer.toJson(targetDate), 'importance': serializer .toJson($GoalTable.$converterimportance.toJson(importance)), @@ -962,15 +1000,19 @@ class GoalDTO extends DataClass implements Insertable { GoalDTO copyWith( {int? id, String? name, - double? targetAmount, + double? amount, + DateTime? date, double? inflation, + double? targetAmount, DateTime? targetDate, GoalImportance? importance}) => GoalDTO( id: id ?? this.id, name: name ?? this.name, - targetAmount: targetAmount ?? this.targetAmount, + amount: amount ?? this.amount, + date: date ?? this.date, inflation: inflation ?? this.inflation, + targetAmount: targetAmount ?? this.targetAmount, targetDate: targetDate ?? this.targetDate, importance: importance ?? this.importance, ); @@ -979,8 +1021,10 @@ class GoalDTO extends DataClass implements Insertable { return (StringBuffer('GoalDTO(') ..write('id: $id, ') ..write('name: $name, ') - ..write('targetAmount: $targetAmount, ') + ..write('amount: $amount, ') + ..write('date: $date, ') ..write('inflation: $inflation, ') + ..write('targetAmount: $targetAmount, ') ..write('targetDate: $targetDate, ') ..write('importance: $importance') ..write(')')) @@ -988,16 +1032,18 @@ class GoalDTO extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, targetAmount, inflation, targetDate, importance); + int get hashCode => Object.hash( + id, name, amount, date, inflation, targetAmount, targetDate, importance); @override bool operator ==(Object other) => identical(this, other) || (other is GoalDTO && other.id == this.id && other.name == this.name && - other.targetAmount == this.targetAmount && + other.amount == this.amount && + other.date == this.date && other.inflation == this.inflation && + other.targetAmount == this.targetAmount && other.targetDate == this.targetDate && other.importance == this.importance); } @@ -1005,43 +1051,55 @@ class GoalDTO extends DataClass implements Insertable { class GoalCompanion extends UpdateCompanion { final Value id; final Value name; - final Value targetAmount; + final Value amount; + final Value date; final Value inflation; + final Value targetAmount; final Value targetDate; final Value importance; const GoalCompanion({ this.id = const Value.absent(), this.name = const Value.absent(), - this.targetAmount = const Value.absent(), + this.amount = const Value.absent(), + this.date = const Value.absent(), this.inflation = const Value.absent(), + this.targetAmount = const Value.absent(), this.targetDate = const Value.absent(), this.importance = const Value.absent(), }); GoalCompanion.insert({ this.id = const Value.absent(), required String name, - required double targetAmount, + required double amount, + required DateTime date, required double inflation, + required double targetAmount, required DateTime targetDate, required GoalImportance importance, }) : name = Value(name), - targetAmount = Value(targetAmount), + amount = Value(amount), + date = Value(date), inflation = Value(inflation), + targetAmount = Value(targetAmount), targetDate = Value(targetDate), importance = Value(importance); static Insertable custom({ Expression? id, Expression? name, - Expression? targetAmount, + Expression? amount, + Expression? date, Expression? inflation, + Expression? targetAmount, Expression? targetDate, Expression? importance, }) { return RawValuesInsertable({ if (id != null) 'ID': id, if (name != null) 'NAME': name, - if (targetAmount != null) 'TARGET_AMOUNT': targetAmount, + if (amount != null) 'AMOUNT': amount, + if (date != null) 'DATE': date, if (inflation != null) 'INFLATION': inflation, + if (targetAmount != null) 'TARGET_AMOUNT': targetAmount, if (targetDate != null) 'TARGET_DATE': targetDate, if (importance != null) 'IMPORTANCE': importance, }); @@ -1050,15 +1108,19 @@ class GoalCompanion extends UpdateCompanion { GoalCompanion copyWith( {Value? id, Value? name, - Value? targetAmount, + Value? amount, + Value? date, Value? inflation, + Value? targetAmount, Value? targetDate, Value? importance}) { return GoalCompanion( id: id ?? this.id, name: name ?? this.name, - targetAmount: targetAmount ?? this.targetAmount, + amount: amount ?? this.amount, + date: date ?? this.date, inflation: inflation ?? this.inflation, + targetAmount: targetAmount ?? this.targetAmount, targetDate: targetDate ?? this.targetDate, importance: importance ?? this.importance, ); @@ -1073,12 +1135,18 @@ class GoalCompanion extends UpdateCompanion { if (name.present) { map['NAME'] = Variable(name.value); } - if (targetAmount.present) { - map['TARGET_AMOUNT'] = Variable(targetAmount.value); + if (amount.present) { + map['AMOUNT'] = Variable(amount.value); + } + if (date.present) { + map['DATE'] = Variable(date.value); } if (inflation.present) { map['INFLATION'] = Variable(inflation.value); } + if (targetAmount.present) { + map['TARGET_AMOUNT'] = Variable(targetAmount.value); + } if (targetDate.present) { map['TARGET_DATE'] = Variable(targetDate.value); } @@ -1094,8 +1162,10 @@ class GoalCompanion extends UpdateCompanion { return (StringBuffer('GoalCompanion(') ..write('id: $id, ') ..write('name: $name, ') - ..write('targetAmount: $targetAmount, ') + ..write('amount: $amount, ') + ..write('date: $date, ') ..write('inflation: $inflation, ') + ..write('targetAmount: $targetAmount, ') ..write('targetDate: $targetDate, ') ..write('importance: $importance') ..write(')')) diff --git a/lib/app_router.dart b/lib/app_router.dart index 7d59f3b..4e2a046 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -1,12 +1,15 @@ import 'package:flutter/material.dart'; import 'package:wealth_wave/ui/nav_path.dart'; +import 'package:wealth_wave/ui/pages/create_goal_page.dart'; import 'package:wealth_wave/ui/pages/main_page.dart'; class AppRouter { static Widget route(String path) { - Uri uri = Uri.parse(path); + final uri = Uri.parse(path); if (NavPath.isMainPagePath(uri.pathSegments)) { return MainPage(path: uri.pathSegments); + } else if (NavPath.isCreateGoalPagePath(uri.pathSegments)) { + return const CreateGoalPage(); } return const MainPage(path: []); } diff --git a/lib/core/single_event.dart b/lib/core/single_event.dart index 8608c20..08f0fc7 100644 --- a/lib/core/single_event.dart +++ b/lib/core/single_event.dart @@ -1,21 +1,21 @@ class SingleEvent { - final T content; + final T _content; bool _isConsumed = false; - SingleEvent(this.content); + SingleEvent(this._content); void consume(Function(T) action) { if (!_isConsumed) { _isConsumed = true; - action(content); + action(_content); } } T? consumeGet() { if (!_isConsumed) { _isConsumed = true; - return content; + return _content; } return null; } @@ -25,6 +25,6 @@ class SingleEvent { } T peekContent() { - return content; + return _content; } } diff --git a/lib/domain/use_cases/goal/create_goal_use_case.dart b/lib/domain/use_cases/goal/create_goal_use_case.dart index 04ea3ba..65ec430 100644 --- a/lib/domain/use_cases/goal/create_goal_use_case.dart +++ b/lib/domain/use_cases/goal/create_goal_use_case.dart @@ -8,12 +8,16 @@ class CreateGoalUseCase { Future createGoal( {required final String name, + required final double amount, + required final DateTime date, + required final double inflation, required final double targetAmount, required final DateTime targetDate, - required final double inflation, required final GoalImportance importance}) { return _goalApi.createGoal( name: name, + amount: amount, + date: date, targetAmount: targetAmount, targetDate: targetDate, inflation: inflation, diff --git a/lib/domain/use_cases/goal/get_goal_list_use_case.dart b/lib/domain/use_cases/goal/get_goal_list_use_case.dart index a071b8c..19d7f34 100644 --- a/lib/domain/use_cases/goal/get_goal_list_use_case.dart +++ b/lib/domain/use_cases/goal/get_goal_list_use_case.dart @@ -7,9 +7,9 @@ class GetGoalListUseCase { GetGoalListUseCase({final GoalApi? goalApi}) : _goalApi = goalApi ?? GoalApi(); - Future> getGoals() { + Stream> getGoals() { return _goalApi .getGoals() - .then((value) => value.map((e) => Goal.from(e)).toList()); + .map((event) => event.map((e) => Goal.from(e)).toList()); } } diff --git a/lib/presentation/create_goal_page_presenter.dart b/lib/presentation/create_goal_page_presenter.dart index 2827ffb..0371b01 100644 --- a/lib/presentation/create_goal_page_presenter.dart +++ b/lib/presentation/create_goal_page_presenter.dart @@ -2,89 +2,89 @@ import 'dart:math'; import 'package:wealth_wave/contract/goal_importance.dart'; import 'package:wealth_wave/core/presenter.dart'; +import 'package:wealth_wave/core/single_event.dart'; import 'package:wealth_wave/domain/use_cases/goal/create_goal_use_case.dart'; -import 'package:wealth_wave/domain/use_cases/goal/get_goal_use_case.dart'; -import 'package:wealth_wave/domain/use_cases/goal/update_goal_use_case.dart'; class CreateGoalPagePresenter extends Presenter { final CreateGoalUseCase _createGoalUseCase; - final UpdateGoalUseCase _updateGoalUseCase; - final GetGoalUseCase _getGoalUseCase; CreateGoalPagePresenter({ final CreateGoalUseCase? createGoalUseCase, - final GetGoalUseCase? getGoalUseCase, - final UpdateGoalUseCase? updateGoalUseCase, }) : _createGoalUseCase = createGoalUseCase ?? CreateGoalUseCase(), - _getGoalUseCase = getGoalUseCase ?? GetGoalUseCase(), - _updateGoalUseCase = updateGoalUseCase ?? UpdateGoalUseCase(), super(CreateGoalPageViewState()); - void fetchGoal({required final int id}) { - _getGoalUseCase.getGoal(id: id).then((goal) => { - updateViewState((viewState) { - viewState.id = goal.id; - viewState.name = goal.name; - viewState.targetAmount = goal.targetAmount; - viewState.targetDate = goal.targetDate; - viewState.inflation = goal.inflation; - viewState.importance = goal.importance; - }) - }); - } + void createGoal() { + var viewState = getViewState(); + + if (!viewState.isValid()) { + return; + } + + final name = viewState.name; + final amount = viewState.amount; + final date = DateTime.now(); + final targetAmount = viewState.getTargetAmount(); + final targetDate = viewState.targetDate; + final inflation = viewState.inflation; + final importance = viewState.importance; - void createGoal( - {required final String name, - required final double targetAmount, - required final DateTime targetDate, - required final double inflation, - required final GoalImportance importance}) { _createGoalUseCase .createGoal( name: name, + amount: amount, + date: date, targetAmount: targetAmount, targetDate: targetDate, inflation: inflation, importance: importance) - .then((_) => - updateViewState((viewState) => viewState.onCompleted = true)); + .then((_) => updateViewState( + (viewState) => viewState.onGoalCreated = SingleEvent(null))); } - void updateGoal( - {required final int id, - required final String name, - required final double targetAmount, - required final DateTime targetDate, - required final double inflation, - required final GoalImportance importance}) { - _updateGoalUseCase - .update( - id: id, - name: name, - targetAmount: targetAmount, - targetDate: targetDate, - inflation: inflation, - importance: importance) - .then((_) => - updateViewState((viewState) => viewState.onCompleted = true)); + void nameChanged(String text) { + updateViewState((viewState) => viewState.name = text); + } + + void amountChanged(String text) { + updateViewState( + (viewState) => viewState.amount = double.tryParse(text) ?? 0); + } + + void targetDateChanged(DateTime date) { + updateViewState((viewState) => viewState.targetDate = date); + } + + void inflationChanged(double text) { + updateViewState((viewState) => viewState.inflation = text); + } + + void importanceChanged(GoalImportance importance) { + updateViewState((viewState) => viewState.importance = importance); } } class CreateGoalPageViewState { - int? id; - String? name; - double? targetAmount; - DateTime? targetDate; - double? inflation; - GoalImportance? importance; - bool onCompleted = false; + String name = ''; + double amount = 0.0; + DateTime targetDate = DateTime.now().add(const Duration(days: 365)); + double inflation = 0; + GoalImportance importance = GoalImportance.high; + SingleEvent? onGoalCreated; double getTargetAmount() { - var amount = targetAmount ?? 0; - var inflation = this.inflation ?? 0; - var targetDate = this.targetDate ?? DateTime.now(); + final amount = this.amount; + final inflation = this.inflation; + final targetDate = this.targetDate; return amount * pow(1 + inflation / 100, targetDate.difference(DateTime.now()).inDays / 365); } + + bool isValid() { + return name.isNotEmpty && + amount > 0 && + getTargetAmount() > 0 && + targetDate.isAfter(DateTime.now()) && + inflation >= 0; + } } diff --git a/lib/presentation/goals_page_presenter.dart b/lib/presentation/goals_page_presenter.dart index 40c6ddd..23822c4 100644 --- a/lib/presentation/goals_page_presenter.dart +++ b/lib/presentation/goals_page_presenter.dart @@ -19,11 +19,9 @@ class GoalsPagePresenter extends Presenter { super(GoalsPageViewState()); void fetchGoals() { - _getGoalsUseCase.getGoals().then((goals) => { - updateViewState((viewState) { - viewState.goals = goals; - }) - }); + _getGoalsUseCase.getGoals().listen((goals) => updateViewState((viewState) { + viewState.goals = goals; + })); } void deleteGoal({required final int id}) { diff --git a/lib/ui/app_dimen.dart b/lib/ui/app_dimen.dart new file mode 100644 index 0000000..400c0db --- /dev/null +++ b/lib/ui/app_dimen.dart @@ -0,0 +1,6 @@ +class AppDimen { + AppDimen._(); + + static const double defaultPadding = 24.0; + static const double minPadding = 8.0; +} diff --git a/lib/ui/pages/create_goal_page.dart b/lib/ui/pages/create_goal_page.dart new file mode 100644 index 0000000..de93f1f --- /dev/null +++ b/lib/ui/pages/create_goal_page.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:wealth_wave/contract/goal_importance.dart'; +import 'package:wealth_wave/core/page_state.dart'; +import 'package:wealth_wave/presentation/create_goal_page_presenter.dart'; +import 'package:wealth_wave/ui/app_dimen.dart'; +import 'package:wealth_wave/ui/nav_path.dart'; + +class CreateGoalPage extends StatefulWidget { + const CreateGoalPage({super.key}); + + @override + State createState() => _CreateGoalPage(); +} + +class _CreateGoalPage extends PageState { + final _nameController = TextEditingController(); + final _amountController = TextEditingController(); + final _targetDateController = TextEditingController(); + + @override + void initState() { + super.initState(); + _nameController.addListener(() { + presenter.nameChanged(_nameController.text); + }); + + _amountController.addListener(() { + presenter.amountChanged(_amountController.text); + }); + } + + @override + Widget buildWidget(BuildContext context, CreateGoalPageViewState snapshot) { + WidgetsBinding.instance.addPostFrameCallback((_) { + snapshot.onGoalCreated?.consume((_) { + Navigator.of(context).pop(); + }); + }); + + return Scaffold( + body: Center( + child: SizedBox( + width: 400, + child: Card( + child: Form( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('Create Basket', + style: Theme.of(context).textTheme.headlineMedium), + Padding( + padding: const EdgeInsets.all(AppDimen.minPadding), + child: TextFormField( + textCapitalization: TextCapitalization.words, + controller: _nameController, + decoration: const InputDecoration(hintText: 'Name'), + ), + ), + Padding( + padding: const EdgeInsets.all(AppDimen.minPadding), + child: TextFormField( + controller: _amountController, + keyboardType: TextInputType.number, + decoration: const InputDecoration(hintText: 'Amount'), + ), + ), + Padding( + padding: const EdgeInsets.all(AppDimen.minPadding), + child: TextFormField( + controller: _targetDateController, + readOnly: true, + onTap: () async { + var date = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime(2100)); + if (date != null) { + _targetDateController.text = + DateFormat('dd-MM-yyyy').format(date); + ; + presenter.targetDateChanged(date); + } + }, + decoration: + const InputDecoration(hintText: 'Target Date'), + ), + ), + Padding( + padding: const EdgeInsets.all(AppDimen.minPadding), + child: Slider( + label: 'Inflation', + min: 0, + max: 20, + divisions: 20, + value: (snapshot.inflation ?? 0), + onChanged: (value) { + presenter.inflationChanged(value); + }), + ), + Text( + "Inflation: ${(snapshot.inflation ?? 0)}", + style: Theme.of(context).textTheme.labelMedium, + ), + Padding( + padding: const EdgeInsets.all(AppDimen.minPadding), + child: DropdownButtonFormField( + hint: const Text('Importance'), + value: snapshot.importance, + onChanged: (value) { + if (value != null) { + presenter.importanceChanged(value); + } + }, + items: const [ + DropdownMenuItem( + value: GoalImportance.high, + child: Text('High'), + ), + DropdownMenuItem( + value: GoalImportance.medium, + child: Text('Medium'), + ), + DropdownMenuItem( + value: GoalImportance.low, + child: Text('Low'), + ), + ], + ), + ), + FilledButton( + onPressed: snapshot.isValid() + ? () { + presenter.createGoal(); + } + : null, + child: const Text('Create'), + ), + ], + ), + )))), + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).pushNamed(NavPath.createGoal); + }, + tooltip: 'Add', + child: const Icon(Icons.add), + ), + ); + } + + @override + CreateGoalPagePresenter initializePresenter() { + return CreateGoalPagePresenter(); + } +} diff --git a/lib/ui/pages/main_page.dart b/lib/ui/pages/main_page.dart index dbc7c8a..3b1717e 100644 --- a/lib/ui/pages/main_page.dart +++ b/lib/ui/pages/main_page.dart @@ -12,7 +12,7 @@ class MainPage extends StatefulWidget { } class _MainPageState extends State { - int _selectedIndex = 0; + var _selectedIndex = 0; @override Widget build(BuildContext context) { diff --git a/pubspec.lock b/pubspec.lock index 205a0e8..f9426b2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: code_builder - sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f + sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.9.0" collection: dependency: transitive description: @@ -307,6 +307,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" io: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 904cc60..bb9f0ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: sqlite3_flutter_libs: ^0.5.0 path_provider: ^2.0.0 path: ^1.8.3 + intl: ^0.17.0 dev_dependencies: flutter_test: