diff --git a/.gitignore b/.gitignore
index 0122a41..99d7ef4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,8 @@ app.*.symbols
/lib/main.config.dart
*.freezed.dart
*.g.dart
-*/di.config.dart
+*.config.dart
*.mocks.dart
lib/core/di/di.config.dart
+*.gr.dart
+
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 7c56964..1dc6cf7 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 12.0
+ 13.0
diff --git a/ios/Podfile b/ios/Podfile
index e549ee2..620e46e 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '12.0'
+# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 5d5df2f..65a07d2 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -27,11 +27,11 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
SPEC CHECKSUMS:
- Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
-PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5
+PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
COCOAPODS: 1.15.2
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 40e99ab..8f07c66 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -455,7 +455,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -585,7 +585,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -636,7 +636,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/lib/core/designsystem/atoms/colors.dart b/lib/core/designsystem/atoms/colors.dart
index d2383d8..d620da6 100644
--- a/lib/core/designsystem/atoms/colors.dart
+++ b/lib/core/designsystem/atoms/colors.dart
@@ -4,3 +4,6 @@ const primary = Color(0xFF1338BE);
const disabled = Color(0xFF565656);
const onBorder = Color(0xFF0D5EAF);
const onBackground = Color(0xFFC6E0FA);
+const background = Color(0xFF3D67FF);
+const error = Color.fromARGB(255, 196, 8, 8);
+const primaryText = Color(0xFF110000);
diff --git a/lib/core/designsystem/molecules/buttons/primary_button.dart b/lib/core/designsystem/molecules/buttons/primary_button.dart
index 87ac8e3..70e42c6 100644
--- a/lib/core/designsystem/molecules/buttons/primary_button.dart
+++ b/lib/core/designsystem/molecules/buttons/primary_button.dart
@@ -4,6 +4,7 @@ import 'package:mibook/core/designsystem/atoms/colors.dart';
class PrimaryButton extends StatelessWidget {
final String title;
final bool isEnabled;
+ final bool isExpanded;
final bool isLoading;
final VoidCallback onPressed;
@@ -11,6 +12,7 @@ class PrimaryButton extends StatelessWidget {
super.key,
required this.title,
this.isEnabled = true,
+ this.isExpanded = false,
this.isLoading = false,
required this.onPressed,
});
@@ -19,6 +21,7 @@ class PrimaryButton extends StatelessWidget {
Widget build(BuildContext context) {
return SizedBox(
height: 48,
+ width: isExpanded ? double.infinity : null,
child: ElevatedButton(
onPressed: isEnabled ? onPressed : null,
style: ElevatedButton.styleFrom(
diff --git a/lib/core/designsystem/molecules/buttons/secondary_button.dart b/lib/core/designsystem/molecules/buttons/secondary_button.dart
new file mode 100644
index 0000000..163f709
--- /dev/null
+++ b/lib/core/designsystem/molecules/buttons/secondary_button.dart
@@ -0,0 +1,58 @@
+import 'package:flutter/material.dart';
+import 'package:mibook/core/designsystem/atoms/colors.dart';
+
+class SecondaryButton extends StatelessWidget {
+ final String title;
+ final bool isEnabled;
+ final bool isExpanded;
+ final bool isLoading;
+ final VoidCallback onPressed;
+
+ const SecondaryButton({
+ super.key,
+ required this.title,
+ this.isEnabled = true,
+ this.isExpanded = false,
+ this.isLoading = false,
+ required this.onPressed,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 48,
+ width: isExpanded ? double.infinity : null,
+ child: OutlinedButton(
+ onPressed: isEnabled ? onPressed : null,
+ style: OutlinedButton.styleFrom(
+ side: BorderSide(
+ color: isEnabled ? primary : disabled,
+ width: 1.0,
+ ),
+ foregroundColor: isEnabled ? primary : disabled,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 12,
+ ),
+ textStyle: const TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ ),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(16),
+ ),
+ minimumSize: const Size(80, 48),
+ ),
+ child: isLoading
+ ? SizedBox(
+ width: 20,
+ height: 20,
+ child: CircularProgressIndicator(
+ color: Colors.white,
+ ),
+ )
+ : Text(title),
+ ),
+ );
+ }
+}
diff --git a/lib/core/designsystem/molecules/indicators/progress_stepper.dart b/lib/core/designsystem/molecules/indicators/progress_stepper.dart
new file mode 100644
index 0000000..39f108b
--- /dev/null
+++ b/lib/core/designsystem/molecules/indicators/progress_stepper.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/material.dart';
+import 'package:mibook/core/designsystem/atoms/colors.dart';
+
+class ProgressStepper extends StatelessWidget {
+ final double progress;
+
+ const ProgressStepper({super.key, required this.progress});
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('Progress: ${(progress * 100).toStringAsFixed(1)}%'),
+ const SizedBox(height: 8),
+ LinearProgressIndicator(
+ value: progress,
+ minHeight: 8,
+ backgroundColor: Colors.grey[300],
+ valueColor: AlwaysStoppedAnimation(background),
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/core/designsystem/molecules/indicators/radio_box.dart b/lib/core/designsystem/molecules/indicators/radio_box.dart
new file mode 100644
index 0000000..0ab17a3
--- /dev/null
+++ b/lib/core/designsystem/molecules/indicators/radio_box.dart
@@ -0,0 +1,32 @@
+import 'package:flutter/material.dart';
+import 'package:mibook/core/designsystem/atoms/colors.dart';
+
+class RadioBox extends StatelessWidget {
+ final bool value;
+ final ValueChanged onChanged;
+ final String label;
+
+ const RadioBox({
+ super.key,
+ required this.value,
+ required this.onChanged,
+ required this.label,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: () => onChanged(!value),
+ child: Row(
+ children: [
+ Checkbox(
+ fillColor: WidgetStateProperty.all(primary),
+ value: value,
+ onChanged: onChanged,
+ ),
+ Text(label),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/core/designsystem/molecules/inputfields/input_field.dart b/lib/core/designsystem/molecules/inputfields/input_field.dart
index b112c54..f8ffc4e 100644
--- a/lib/core/designsystem/molecules/inputfields/input_field.dart
+++ b/lib/core/designsystem/molecules/inputfields/input_field.dart
@@ -6,6 +6,10 @@ class InputField extends StatelessWidget {
final TextEditingController controller;
final bool isEnabled;
final String placeholder;
+ final String? suffixText;
+ final String? prefixText;
+ final String? errorMessage;
+ final TextInputType? keyboardType;
final Function(String) onChanged;
const InputField({
@@ -14,6 +18,10 @@ class InputField extends StatelessWidget {
required this.controller,
this.isEnabled = true,
this.placeholder = '',
+ this.suffixText,
+ this.prefixText,
+ this.errorMessage,
+ this.keyboardType,
required this.onChanged,
});
@@ -22,19 +30,34 @@ class InputField extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if (label.isNotEmpty) Text(label),
+ if (label.isNotEmpty)
+ Text(
+ label,
+ style: TextStyle(
+ color: errorMessage == null ? primaryText : error,
+ ),
+ ),
const SizedBox(height: 8),
- SizedBox(
- height: 48,
- child: TextField(
- decoration: InputDecoration(
- border: OutlineInputBorder(),
- focusedBorder: OutlineInputBorder(
- borderSide: BorderSide(color: onBorder),
- ),
+ TextField(
+ keyboardType: keyboardType,
+ decoration: InputDecoration(
+ border: OutlineInputBorder(),
+ focusedBorder: OutlineInputBorder(
+ borderSide: BorderSide(color: onBorder),
+ ),
+ errorBorder: OutlineInputBorder(
+ borderSide: BorderSide(color: error),
+ ),
+ hintText: placeholder,
+ suffixText: suffixText,
+ prefixText: prefixText,
+ errorText: errorMessage,
+ contentPadding: const EdgeInsets.symmetric(
+ vertical: 16, // controls height
+ horizontal: 16,
),
- onChanged: onChanged,
),
+ onChanged: onChanged,
),
],
);
diff --git a/lib/core/designsystem/organisms/app_nav_bar.dart b/lib/core/designsystem/organisms/app_nav_bar.dart
index 4ca346e..a8928cc 100644
--- a/lib/core/designsystem/organisms/app_nav_bar.dart
+++ b/lib/core/designsystem/organisms/app_nav_bar.dart
@@ -1,3 +1,4 @@
+import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:mibook/core/designsystem/modifiers/display_as_loader.dart';
@@ -65,12 +66,19 @@ class AppNavBar extends StatelessWidget implements PreferredSizeWidget {
Widget _title() {
return DisplayAsLoader(
isLoading: isTitleLoading,
- child: Text(
- titleText,
- style: TextStyle(
- color: Colors.black,
- fontSize: titleText.length > 20 ? 14 : 22,
- fontWeight: FontWeight.bold,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxWidth: 250),
+ child: AutoSizeText(
+ titleText,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Colors.black,
+ fontSize: 22,
+ fontWeight: FontWeight.bold,
+ ),
+ minFontSize: 12,
+ stepGranularity: 1,
),
),
);
diff --git a/lib/core/designsystem/organisms/list_item.dart b/lib/core/designsystem/organisms/list_item.dart
index d524803..82a0e6d 100644
--- a/lib/core/designsystem/organisms/list_item.dart
+++ b/lib/core/designsystem/organisms/list_item.dart
@@ -73,11 +73,13 @@ class GenericInput extends ListItemInput {
class ListItem extends StatelessWidget {
final ListItemInput input;
+ final bool isExpanded;
final Function()? onTap;
const ListItem({
super.key,
required this.input,
+ this.isExpanded = false,
this.onTap,
});
@@ -86,6 +88,7 @@ class ListItem extends StatelessWidget {
return InkWell(
onTap: onTap,
child: Container(
+ width: isExpanded ? double.infinity : null,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: onBackground,
diff --git a/lib/core/routes/app_router.dart b/lib/core/routes/app_router.dart
index 3771925..9d79f0d 100644
--- a/lib/core/routes/app_router.dart
+++ b/lib/core/routes/app_router.dart
@@ -18,7 +18,8 @@ class AppRouter extends RootStackRouter {
page: SearchRoute.page,
children: [
AutoRoute(page: BookSearchRoute.page),
- AutoRoute(page: BoolDetailsRoute.page),
+ AutoRoute(page: BookDetailsRoute.page),
+ AutoRoute(page: StartReadingRoute.page),
],
),
AutoRoute(
diff --git a/lib/core/routes/app_router.gr.dart b/lib/core/routes/app_router.gr.dart
deleted file mode 100644
index 7e0406f..0000000
--- a/lib/core/routes/app_router.gr.dart
+++ /dev/null
@@ -1,196 +0,0 @@
-// dart format width=80
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// **************************************************************************
-// AutoRouterGenerator
-// **************************************************************************
-
-// ignore_for_file: type=lint
-// coverage:ignore-file
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'package:auto_route/auto_route.dart' as _i10;
-import 'package:flutter/material.dart' as _i11;
-import 'package:mibook/layers/presentation/navigation/dashboard/dashboard_page.dart'
- as _i4;
-import 'package:mibook/layers/presentation/navigation/tabs/home_tab.dart'
- as _i5;
-import 'package:mibook/layers/presentation/navigation/tabs/objective_tab.dart'
- as _i6;
-import 'package:mibook/layers/presentation/navigation/tabs/search_tab.dart'
- as _i9;
-import 'package:mibook/layers/presentation/screens/bookdetails/bool_details_page.dart'
- as _i2;
-import 'package:mibook/layers/presentation/screens/booksearch/book_search_page.dart'
- as _i1;
-import 'package:mibook/layers/presentation/screens/currentobjective/current_objective_page.dart'
- as _i3;
-import 'package:mibook/layers/presentation/screens/onboarding/pre_onboarding_page.dart'
- as _i7;
-import 'package:mibook/layers/presentation/screens/readinglist/reading_list_page.dart'
- as _i8;
-
-/// generated route for
-/// [_i1.BookSearchPage]
-class BookSearchRoute extends _i10.PageRouteInfo {
- const BookSearchRoute({List<_i10.PageRouteInfo>? children})
- : super(BookSearchRoute.name, initialChildren: children);
-
- static const String name = 'BookSearchRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i1.BookSearchPage();
- },
- );
-}
-
-/// generated route for
-/// [_i2.BoolDetailsPage]
-class BoolDetailsRoute extends _i10.PageRouteInfo {
- BoolDetailsRoute({
- _i11.Key? key,
- required String id,
- List<_i10.PageRouteInfo>? children,
- }) : super(
- BoolDetailsRoute.name,
- args: BoolDetailsRouteArgs(key: key, id: id),
- initialChildren: children,
- );
-
- static const String name = 'BoolDetailsRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- final args = data.argsAs();
- return _i2.BoolDetailsPage(key: args.key, id: args.id);
- },
- );
-}
-
-class BoolDetailsRouteArgs {
- const BoolDetailsRouteArgs({this.key, required this.id});
-
- final _i11.Key? key;
-
- final String id;
-
- @override
- String toString() {
- return 'BoolDetailsRouteArgs{key: $key, id: $id}';
- }
-}
-
-/// generated route for
-/// [_i3.CurrentObjectivePage]
-class CurrentObjectiveRoute extends _i10.PageRouteInfo {
- const CurrentObjectiveRoute({List<_i10.PageRouteInfo>? children})
- : super(CurrentObjectiveRoute.name, initialChildren: children);
-
- static const String name = 'CurrentObjectiveRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i3.CurrentObjectivePage();
- },
- );
-}
-
-/// generated route for
-/// [_i4.DashboardPage]
-class DashboardRoute extends _i10.PageRouteInfo {
- const DashboardRoute({List<_i10.PageRouteInfo>? children})
- : super(DashboardRoute.name, initialChildren: children);
-
- static const String name = 'DashboardRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i4.DashboardPage();
- },
- );
-}
-
-/// generated route for
-/// [_i5.HomeTab]
-class HomeRoute extends _i10.PageRouteInfo {
- const HomeRoute({List<_i10.PageRouteInfo>? children})
- : super(HomeRoute.name, initialChildren: children);
-
- static const String name = 'HomeRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i5.HomeTab();
- },
- );
-}
-
-/// generated route for
-/// [_i6.ObjectiveTab]
-class ObjectiveRoute extends _i10.PageRouteInfo {
- const ObjectiveRoute({List<_i10.PageRouteInfo>? children})
- : super(ObjectiveRoute.name, initialChildren: children);
-
- static const String name = 'ObjectiveRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i6.ObjectiveTab();
- },
- );
-}
-
-/// generated route for
-/// [_i7.PreOnboardingPage]
-class PreOnboardingRoute extends _i10.PageRouteInfo {
- const PreOnboardingRoute({List<_i10.PageRouteInfo>? children})
- : super(PreOnboardingRoute.name, initialChildren: children);
-
- static const String name = 'PreOnboardingRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i7.PreOnboardingPage();
- },
- );
-}
-
-/// generated route for
-/// [_i8.ReadingListPage]
-class ReadingListRoute extends _i10.PageRouteInfo {
- const ReadingListRoute({List<_i10.PageRouteInfo>? children})
- : super(ReadingListRoute.name, initialChildren: children);
-
- static const String name = 'ReadingListRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i8.ReadingListPage();
- },
- );
-}
-
-/// generated route for
-/// [_i9.SearchTab]
-class SearchRoute extends _i10.PageRouteInfo {
- const SearchRoute({List<_i10.PageRouteInfo>? children})
- : super(SearchRoute.name, initialChildren: children);
-
- static const String name = 'SearchRoute';
-
- static _i10.PageInfo page = _i10.PageInfo(
- name,
- builder: (data) {
- return const _i9.SearchTab();
- },
- );
-}
diff --git a/lib/core/utils/strings.dart b/lib/core/utils/strings.dart
index 0669853..0be5186 100644
--- a/lib/core/utils/strings.dart
+++ b/lib/core/utils/strings.dart
@@ -2,3 +2,12 @@ const home = 'Home';
const search = 'Search';
const objectives = 'Objectives';
const searchBooks = 'Search Books';
+const startReading = 'Start Reading';
+const percent = '%';
+const page = 'Page';
+const progress = 'Progress';
+const confirm = 'Confirm';
+const progressErrorMessage = 'You can t enter more pages than the book maximum';
+const zero = '0';
+const finishedBook = 'I finished this book';
+const finishBook = 'Finish Book';
diff --git a/lib/layers/data/datasource/reading_data_source.dart b/lib/layers/data/datasource/reading_data_source.dart
new file mode 100644
index 0000000..0067fb0
--- /dev/null
+++ b/lib/layers/data/datasource/reading_data_source.dart
@@ -0,0 +1,22 @@
+import 'package:injectable/injectable.dart';
+import 'package:mibook/layers/data/models/reading_data.dart';
+
+abstract class IReadingDataSource {
+ Future startReading({
+ required ReadingData readingData,
+ });
+ Future> getReadingData();
+}
+
+@Injectable(as: IReadingDataSource)
+class ReadingDataSource implements IReadingDataSource {
+ @override
+ Future startReading({
+ required ReadingData readingData,
+ }) async {
+ // TO DO
+ }
+
+ @override
+ Future> getReadingData() async => [];
+}
diff --git a/lib/layers/data/models/book_list_data.dart b/lib/layers/data/models/book_list_data.dart
index e89e5e5..7e48e66 100644
--- a/lib/layers/data/models/book_list_data.dart
+++ b/lib/layers/data/models/book_list_data.dart
@@ -6,7 +6,7 @@ part 'book_list_data.g.dart';
@JsonSerializable()
class BookListData {
final String kind;
- final int totalItems;
+ final int? totalItems;
@JsonKey(defaultValue: [])
final List items;
@@ -23,8 +23,11 @@ class BookListData {
BookListDomain toDomain() {
return BookListDomain(
- totalItems: totalItems,
- books: items.map((item) => item.toDomain()).toList(),
+ totalItems: totalItems ?? 0,
+ books: items
+ .where((item) => item.volumeInfo.pageCount != null)
+ .map((item) => item.toDomain())
+ .toList(),
);
}
}
@@ -64,6 +67,7 @@ class BookItem {
authors: volumeInfo.authors,
description: volumeInfo.description,
thumbnail: volumeInfo.imageLinks?.thumbnail,
+ pageCount: volumeInfo.pageCount ?? 100,
);
}
}
@@ -102,7 +106,7 @@ class VolumeInfo {
this.description,
this.industryIdentifiers,
this.readingModes,
- this.pageCount,
+ required this.pageCount,
this.printType,
this.categories,
this.averageRating,
@@ -233,7 +237,7 @@ class Price {
@JsonSerializable()
class Offer {
- final int finskyOfferType;
+ final int? finskyOfferType;
final Price? listPrice;
final Price? retailPrice;
final bool? giftable;
diff --git a/lib/layers/data/models/reading_data.dart b/lib/layers/data/models/reading_data.dart
new file mode 100644
index 0000000..6ad64ce
--- /dev/null
+++ b/lib/layers/data/models/reading_data.dart
@@ -0,0 +1,29 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:mibook/layers/domain/models/reading_domain.dart';
+part 'reading_data.g.dart';
+
+@JsonSerializable()
+class ReadingData {
+ final String bookId;
+ final double progress;
+
+ ReadingData({
+ required this.bookId,
+ required this.progress,
+ });
+
+ factory ReadingData.fromJson(Map json) =>
+ _$ReadingDataFromJson(json);
+
+ Map toJson() => _$ReadingDataToJson(this);
+
+ factory ReadingData.fromDomain(ReadingDomain domain) => ReadingData(
+ bookId: domain.bookId,
+ progress: domain.progress,
+ );
+
+ ReadingDomain toDomain() => ReadingDomain(
+ bookId: bookId,
+ progress: progress,
+ );
+}
diff --git a/lib/layers/data/repository/reading_repository.dart b/lib/layers/data/repository/reading_repository.dart
new file mode 100644
index 0000000..a225bbe
--- /dev/null
+++ b/lib/layers/data/repository/reading_repository.dart
@@ -0,0 +1,29 @@
+import 'package:injectable/injectable.dart';
+import 'package:mibook/layers/data/datasource/reading_data_source.dart';
+import 'package:mibook/layers/data/models/reading_data.dart';
+import 'package:mibook/layers/domain/models/reading_domain.dart';
+import 'package:mibook/layers/domain/repository/reading_repository.dart';
+
+@Injectable(as: IReadingRepository)
+class ReadingRepository implements IReadingRepository {
+ final IReadingDataSource _dataSource;
+
+ ReadingRepository(this._dataSource);
+
+ @override
+ Future startReading({
+ required ReadingDomain reading,
+ }) async {
+ final data = ReadingData(
+ bookId: reading.bookId,
+ progress: reading.progress,
+ );
+ await _dataSource.startReading(readingData: data);
+ }
+
+ @override
+ Future> getReadings() async {
+ final data = await _dataSource.getReadingData();
+ return data.map((e) => e.toDomain()).toList();
+ }
+}
diff --git a/lib/layers/data/datasource/search_repository.dart b/lib/layers/data/repository/search_repository.dart
similarity index 100%
rename from lib/layers/data/datasource/search_repository.dart
rename to lib/layers/data/repository/search_repository.dart
diff --git a/lib/layers/domain/models/book_list_domain.dart b/lib/layers/domain/models/book_list_domain.dart
index f106814..c130ea3 100644
--- a/lib/layers/domain/models/book_list_domain.dart
+++ b/lib/layers/domain/models/book_list_domain.dart
@@ -15,6 +15,7 @@ class BookDomain {
final List? authors;
final String? description;
final String? thumbnail;
+ final int pageCount;
BookDomain({
required this.id,
@@ -23,5 +24,6 @@ class BookDomain {
required this.authors,
required this.description,
required this.thumbnail,
+ required this.pageCount,
});
}
diff --git a/lib/layers/domain/models/reading_domain.dart b/lib/layers/domain/models/reading_domain.dart
new file mode 100644
index 0000000..7dca4af
--- /dev/null
+++ b/lib/layers/domain/models/reading_domain.dart
@@ -0,0 +1,9 @@
+class ReadingDomain {
+ final String bookId;
+ final double progress;
+
+ ReadingDomain({
+ required this.bookId,
+ required this.progress,
+ });
+}
diff --git a/lib/layers/domain/repository/reading_repository.dart b/lib/layers/domain/repository/reading_repository.dart
new file mode 100644
index 0000000..899ae66
--- /dev/null
+++ b/lib/layers/domain/repository/reading_repository.dart
@@ -0,0 +1,8 @@
+import 'package:mibook/layers/domain/models/reading_domain.dart';
+
+abstract class IReadingRepository {
+ Future startReading({
+ required ReadingDomain reading,
+ });
+ Future> getReadings();
+}
diff --git a/lib/layers/domain/usecases/start_reading.dart b/lib/layers/domain/usecases/start_reading.dart
new file mode 100644
index 0000000..494bd88
--- /dev/null
+++ b/lib/layers/domain/usecases/start_reading.dart
@@ -0,0 +1,23 @@
+import 'package:injectable/injectable.dart';
+import 'package:mibook/layers/domain/models/reading_domain.dart';
+import 'package:mibook/layers/domain/repository/reading_repository.dart';
+
+abstract class IStartReading {
+ Future call({
+ required ReadingDomain reading,
+ });
+}
+
+@Injectable(as: IStartReading)
+class StartReading implements IStartReading {
+ final IReadingRepository _repository;
+
+ StartReading(this._repository);
+
+ @override
+ Future call({
+ required ReadingDomain reading,
+ }) async {
+ await _repository.startReading(reading: reading);
+ }
+}
diff --git a/lib/layers/presentation/screens/bookdetails/book_details_event.dart b/lib/layers/presentation/screens/bookdetails/book_details_event.dart
index ff95196..cc0b1c3 100644
--- a/lib/layers/presentation/screens/bookdetails/book_details_event.dart
+++ b/lib/layers/presentation/screens/bookdetails/book_details_event.dart
@@ -5,4 +5,12 @@ class DidLoadEvent extends BookDetailsEvent {
DidLoadEvent(this.bookId);
}
-class DidClickStartReadingEvent extends BookDetailsEvent {}
+class DidClickStartReadingEvent extends BookDetailsEvent {
+ final double progress;
+ DidClickStartReadingEvent(this.progress);
+}
+
+class DidChangeProgressTextEvent extends BookDetailsEvent {
+ final int progress;
+ DidChangeProgressTextEvent(this.progress);
+}
diff --git a/lib/layers/presentation/screens/bookdetails/bool_details_page.dart b/lib/layers/presentation/screens/bookdetails/book_details_page.dart
similarity index 55%
rename from lib/layers/presentation/screens/bookdetails/bool_details_page.dart
rename to lib/layers/presentation/screens/bookdetails/book_details_page.dart
index 9ac907b..4fc0629 100644
--- a/lib/layers/presentation/screens/bookdetails/bool_details_page.dart
+++ b/lib/layers/presentation/screens/bookdetails/book_details_page.dart
@@ -1,18 +1,23 @@
import 'package:auto_route/auto_route.dart';
+import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
+import 'package:mibook/core/designsystem/molecules/buttons/primary_button.dart';
import 'package:mibook/core/designsystem/organisms/app_nav_bar.dart';
+import 'package:mibook/core/designsystem/organisms/list_item.dart';
import 'package:mibook/core/di/di.dart';
+import 'package:mibook/core/routes/app_router.gr.dart';
+import 'package:mibook/core/utils/strings.dart' as strings;
import 'package:mibook/layers/presentation/screens/bookdetails/book_details_state.dart';
import 'package:mibook/layers/presentation/screens/bookdetails/book_details_view_model.dart';
import 'package:mibook/layers/presentation/screens/bookdetails/book_details_event.dart';
@RoutePage()
-class BoolDetailsPage extends StatelessWidget {
+class BookDetailsPage extends StatelessWidget {
final String id;
- const BoolDetailsPage({
+ const BookDetailsPage({
super.key,
required this.id,
});
@@ -66,29 +71,52 @@ class _BookDetailsContent extends StatelessWidget {
return Center(child: Text('Error: ${state.errorMessage}'));
} else if (state.bookDetails != null) {
final book = state.bookDetails!;
+
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (book.thumbnail != null)
- Image.network(book.thumbnail!),
+ CachedNetworkImage(imageUrl: book.thumbnail!),
const SizedBox(height: 16),
- Text(
- book.title,
- style: TextStyle(
- fontSize: 24,
- fontWeight: FontWeight.bold,
+ ListItem(
+ isExpanded: true,
+ input: GenericInput(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ book.title,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 8),
+ if (book.authors.isNotEmpty)
+ Text(
+ 'By ${book.authors}',
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ const SizedBox(height: 16),
+ Html(data: book.description),
+ ],
+ ),
),
),
- const SizedBox(height: 8),
- if (book.authors.isNotEmpty)
- Text(
- 'By ${book.authors}',
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- const SizedBox(height: 16),
- Html(data: book.description),
+ const SizedBox(height: 24),
+ PrimaryButton(
+ title: strings.startReading,
+ onPressed: () {
+ if (state.bookDetails != null) {
+ context.router.push(
+ StartReadingRoute(book: state.bookDetails!),
+ );
+ }
+ },
+ ),
+ const SizedBox(height: 32),
],
),
);
diff --git a/lib/layers/presentation/screens/bookdetails/book_details_state.dart b/lib/layers/presentation/screens/bookdetails/book_details_state.dart
index 2e7a4aa..841c49f 100644
--- a/lib/layers/presentation/screens/bookdetails/book_details_state.dart
+++ b/lib/layers/presentation/screens/bookdetails/book_details_state.dart
@@ -10,16 +10,20 @@ class BookDetailsState with _$BookDetailsState {
final String? errorMessage;
@override
final BookDetailsUI? bookDetails;
+ @override
+ final double bookProgress;
BookDetailsState(
this.errorMessage,
this.bookDetails, {
required this.isLoading,
+ required this.bookProgress,
});
factory BookDetailsState.initial() => BookDetailsState(
null,
null,
isLoading: false,
+ bookProgress: 0.0,
);
}
diff --git a/lib/layers/presentation/screens/bookdetails/book_details_ui.dart b/lib/layers/presentation/screens/bookdetails/book_details_ui.dart
index e495b4d..97cf405 100644
--- a/lib/layers/presentation/screens/bookdetails/book_details_ui.dart
+++ b/lib/layers/presentation/screens/bookdetails/book_details_ui.dart
@@ -7,6 +7,7 @@ class BookDetailsUI {
final String authors;
final String description;
final String? thumbnail;
+ final int pageCount;
BookDetailsUI({
required this.id,
@@ -15,6 +16,7 @@ class BookDetailsUI {
required this.authors,
required this.description,
required this.thumbnail,
+ required this.pageCount,
});
factory BookDetailsUI.fromDomain(BookDomain domain) {
@@ -25,6 +27,7 @@ class BookDetailsUI {
authors: (domain.authors ?? []).join(', '),
description: domain.description ?? '',
thumbnail: domain.thumbnail,
+ pageCount: domain.pageCount,
);
}
}
diff --git a/lib/layers/presentation/screens/bookdetails/book_details_view_model.dart b/lib/layers/presentation/screens/bookdetails/book_details_view_model.dart
index 1a7f8de..e89c06d 100644
--- a/lib/layers/presentation/screens/bookdetails/book_details_view_model.dart
+++ b/lib/layers/presentation/screens/bookdetails/book_details_view_model.dart
@@ -1,6 +1,8 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
+import 'package:mibook/layers/domain/models/reading_domain.dart';
import 'package:mibook/layers/domain/usecases/get_book_details.dart';
+import 'package:mibook/layers/domain/usecases/start_reading.dart';
import 'package:mibook/layers/presentation/screens/bookdetails/book_details_event.dart';
import 'package:mibook/layers/presentation/screens/bookdetails/book_details_state.dart';
import 'package:mibook/layers/presentation/screens/bookdetails/book_details_ui.dart';
@@ -8,10 +10,12 @@ import 'package:mibook/layers/presentation/screens/bookdetails/book_details_ui.d
@injectable
class BookDetailsViewModel extends Bloc {
final IGetBookDetails _getBookDetails;
+ final IStartReading _startReading;
String? bookId;
BookDetailsViewModel(
this._getBookDetails,
+ this._startReading,
@factoryParam this.bookId,
) : super(BookDetailsState.initial()) {
on((event, emit) async {
@@ -38,8 +42,13 @@ class BookDetailsViewModel extends Bloc {
);
}
});
- on((event, emit) async {
- // Handle start reading event
+ on((event, emit) async {});
+ on((event, emit) {
+ final pageCount = state.bookDetails?.pageCount;
+ if (pageCount != null) {
+ final progress = event.progress.toDouble() / pageCount.toDouble();
+ emit(state.copyWith(bookProgress: progress));
+ }
});
}
@@ -51,4 +60,15 @@ class BookDetailsViewModel extends Bloc {
return null;
}
}
+
+ Future startReading({required double progress}) async {
+ if (bookId != null) {
+ await _startReading(
+ reading: ReadingDomain(
+ bookId: bookId!,
+ progress: progress,
+ ),
+ );
+ }
+ }
}
diff --git a/lib/layers/presentation/screens/booksearch/book_search_page.dart b/lib/layers/presentation/screens/booksearch/book_search_page.dart
index 4ca0d02..15ff8d6 100644
--- a/lib/layers/presentation/screens/booksearch/book_search_page.dart
+++ b/lib/layers/presentation/screens/booksearch/book_search_page.dart
@@ -110,7 +110,7 @@ class _SearchScaffold extends StatelessWidget {
),
child: ListItem(
onTap: () => context.router.push(
- BoolDetailsRoute(id: book.id),
+ BookDetailsRoute(id: book.id),
),
input: BookItemInput(
id: book.id,
diff --git a/lib/layers/presentation/screens/booksearch/book_ui.dart b/lib/layers/presentation/screens/booksearch/book_ui.dart
index 346cef2..53d5c12 100644
--- a/lib/layers/presentation/screens/booksearch/book_ui.dart
+++ b/lib/layers/presentation/screens/booksearch/book_ui.dart
@@ -7,6 +7,7 @@ class BookUI {
final String authors;
final String description;
final String? thumbnail;
+ final int pageCount;
BookUI({
required this.id,
@@ -15,6 +16,7 @@ class BookUI {
required this.authors,
required this.description,
required this.thumbnail,
+ required this.pageCount,
});
factory BookUI.fromDomain(BookDomain domain) {
@@ -25,6 +27,7 @@ class BookUI {
authors: (domain.authors ?? []).join(', '),
description: domain.description ?? '',
thumbnail: domain.thumbnail,
+ pageCount: domain.pageCount,
);
}
}
diff --git a/lib/layers/presentation/screens/startreading/start_reading_event.dart b/lib/layers/presentation/screens/startreading/start_reading_event.dart
new file mode 100644
index 0000000..013f7cb
--- /dev/null
+++ b/lib/layers/presentation/screens/startreading/start_reading_event.dart
@@ -0,0 +1,10 @@
+abstract class StartReadingEvent {}
+
+class DidEditProgressEvent extends StartReadingEvent {
+ int progress;
+ DidEditProgressEvent({required this.progress});
+}
+
+class DidClickConfirmEvent extends StartReadingEvent {}
+
+class DidClickFinishBookEvent extends StartReadingEvent {}
diff --git a/lib/layers/presentation/screens/startreading/start_reading_page.dart b/lib/layers/presentation/screens/startreading/start_reading_page.dart
new file mode 100644
index 0000000..e275027
--- /dev/null
+++ b/lib/layers/presentation/screens/startreading/start_reading_page.dart
@@ -0,0 +1,142 @@
+import 'package:auto_route/auto_route.dart';
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:mibook/core/designsystem/molecules/buttons/primary_button.dart';
+import 'package:mibook/core/designsystem/molecules/buttons/secondary_button.dart';
+import 'package:mibook/core/designsystem/molecules/indicators/progress_stepper.dart';
+import 'package:mibook/core/designsystem/molecules/inputfields/input_field.dart';
+import 'package:mibook/core/designsystem/organisms/app_nav_bar.dart';
+import 'package:mibook/core/di/di.dart';
+import 'package:mibook/core/utils/strings.dart';
+import 'package:mibook/core/utils/strings.dart' as strings;
+import 'package:mibook/layers/presentation/screens/bookdetails/book_details_ui.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_event.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_state.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_view_model.dart';
+
+@RoutePage()
+class StartReadingPage extends StatelessWidget {
+ final BookDetailsUI book;
+
+ const StartReadingPage({
+ super.key,
+ required this.book,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocProvider.value(
+ value: getIt(param1: book),
+ child: _StartReadingScaffold(),
+ );
+ }
+}
+
+class _StartReadingScaffold extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppNavBar(
+ titleText: startReading,
+ onBack: context.router.maybePop,
+ ),
+ body: BlocBuilder(
+ builder: (context, state) {
+ final viewModel = context.read();
+ if (state.shouldNavigateBack) {
+ context.router.maybePop();
+ }
+ return _StartReadingContent(
+ book: viewModel.book,
+ progress: state.progress,
+ errorMessage: state.inputErrorMessage,
+ onChangeProgressText: (progress) {
+ viewModel.add(
+ DidEditProgressEvent(progress: int.tryParse(progress) ?? 0),
+ );
+ },
+ onClickStartReading: () {
+ viewModel.add(DidClickConfirmEvent());
+ },
+ onClickFinishBook: () {
+ viewModel.add(DidClickFinishBookEvent());
+ },
+ );
+ },
+ ),
+ );
+ }
+}
+
+class _StartReadingContent extends StatelessWidget {
+ final BookDetailsUI book;
+ final double progress;
+ final String? errorMessage;
+ final TextEditingController _controller = TextEditingController();
+ final Function(String) onChangeProgressText;
+ final Function onClickStartReading;
+ final Function onClickFinishBook;
+
+ _StartReadingContent({
+ required this.book,
+ required this.progress,
+ required this.errorMessage,
+ required this.onChangeProgressText,
+ required this.onClickStartReading,
+ required this.onClickFinishBook,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ if (book.thumbnail != null)
+ CachedNetworkImage(imageUrl: book.thumbnail!),
+ const SizedBox(height: 16),
+ Text(
+ book.title,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 8),
+ Text('Maximum pages: ${book.pageCount}'),
+ const SizedBox(height: 24),
+ InputField(
+ keyboardType: TextInputType.number,
+ label: strings.progress,
+ controller: _controller,
+ onChanged: onChangeProgressText,
+ placeholder: strings.zero,
+ errorMessage: errorMessage,
+ ),
+ const SizedBox(height: 24),
+ ProgressStepper(progress: progress),
+ const SizedBox(
+ height: 24,
+ ),
+ const Spacer(),
+ PrimaryButton(
+ title: strings.confirm,
+ isEnabled: errorMessage == null,
+ isExpanded: true,
+ onPressed: onClickStartReading as VoidCallback,
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ SecondaryButton(
+ title: finishBook,
+ isExpanded: true,
+ onPressed: onClickFinishBook as VoidCallback,
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/layers/presentation/screens/startreading/start_reading_state.dart b/lib/layers/presentation/screens/startreading/start_reading_state.dart
new file mode 100644
index 0000000..4036d73
--- /dev/null
+++ b/lib/layers/presentation/screens/startreading/start_reading_state.dart
@@ -0,0 +1,24 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+part 'start_reading_state.freezed.dart';
+
+@freezed
+class StartReadingState with _$StartReadingState {
+ @override
+ final String? inputErrorMessage;
+ @override
+ final double progress;
+ @override
+ final bool shouldNavigateBack;
+
+ StartReadingState({
+ required this.inputErrorMessage,
+ required this.progress,
+ required this.shouldNavigateBack,
+ });
+
+ static StartReadingState get initial => StartReadingState(
+ inputErrorMessage: null,
+ progress: 0.0,
+ shouldNavigateBack: false,
+ );
+}
diff --git a/lib/layers/presentation/screens/startreading/start_reading_view_model.dart b/lib/layers/presentation/screens/startreading/start_reading_view_model.dart
new file mode 100644
index 0000000..c8f1d7e
--- /dev/null
+++ b/lib/layers/presentation/screens/startreading/start_reading_view_model.dart
@@ -0,0 +1,63 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:injectable/injectable.dart';
+import 'package:mibook/core/utils/strings.dart';
+import 'package:mibook/layers/domain/models/reading_domain.dart';
+import 'package:mibook/layers/domain/usecases/start_reading.dart';
+import 'package:mibook/layers/presentation/screens/bookdetails/book_details_ui.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_event.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_state.dart';
+
+@injectable
+class StartReadingViewModel extends Bloc {
+ final IStartReading _startReading;
+ final BookDetailsUI book;
+
+ StartReadingViewModel(
+ this._startReading,
+ @factoryParam this.book,
+ ) : super(StartReadingState.initial) {
+ // Handle DidEditProgress Event
+ on((event, emit) {
+ emit(_didEditProgress(event));
+ });
+ // Handle DidClickConfirm Event
+ on((event, emit) async {
+ final response = await _didClickConfirm();
+ emit(response);
+ });
+ // Handle DidFinishBook Event
+ on((event, emit) async {
+ final response = await _didClickFinishBook();
+ emit(response);
+ });
+ }
+
+ // Event Handler to DidEditProgressEvent
+ StartReadingState _didEditProgress(DidEditProgressEvent event) {
+ final progress = event.progress.toDouble() / book.pageCount.toDouble();
+ if (progress > 1.0) {
+ return state.copyWith(
+ inputErrorMessage: progressErrorMessage,
+ );
+ } else {
+ return state.copyWith(
+ progress: progress,
+ inputErrorMessage: null,
+ );
+ }
+ }
+
+ // Event Handler to DidClickConfirm Event
+ Future _didClickConfirm() async =>
+ _handleStartReading(state.progress);
+
+ // Event Handler to DidClickFinishBook Event
+ Future _didClickFinishBook() async =>
+ _handleStartReading(1.0);
+
+ Future _handleStartReading(double progress) async {
+ final reading = ReadingDomain(bookId: book.id, progress: progress);
+ await _startReading(reading: reading);
+ return state.copyWith(shouldNavigateBack: true);
+ }
+}
diff --git a/macos/Podfile b/macos/Podfile
new file mode 100644
index 0000000..ff5ddb3
--- /dev/null
+++ b/macos/Podfile
@@ -0,0 +1,42 @@
+platform :osx, '10.15'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+
+ flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_macos_build_settings(target)
+ end
+end
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
new file mode 100644
index 0000000..8b72aeb
--- /dev/null
+++ b/macos/Podfile.lock
@@ -0,0 +1,37 @@
+PODS:
+ - FlutterMacOS (1.0.0)
+ - path_provider_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - sqflite_darwin (0.0.4):
+ - Flutter
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - FlutterMacOS (from `Flutter/ephemeral`)
+ - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
+ - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
+
+EXTERNAL SOURCES:
+ FlutterMacOS:
+ :path: Flutter/ephemeral
+ path_provider_foundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
+ shared_preferences_foundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
+ sqflite_darwin:
+ :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
+
+SPEC CHECKSUMS:
+ FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
+ path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
+ sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
+
+PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
+
+COCOAPODS: 1.15.2
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..6e789d1
--- /dev/null
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,801 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+ 86598D225E2B3C9C652FB981 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C44362B8606DEA4742800A /* Pods_Runner.framework */; };
+ C5DD5E3E437B30F2BB9B6CBD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A51812E6CA24747E4C5F14F /* Pods_RunnerTests.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC10EC2044A3C60003C045;
+ remoteInfo = Runner;
+ };
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* mibook.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mibook.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 61747C17440559CA3799386A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ 7A51812E6CA24747E4C5F14F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+ 9F8C8FE9A8D09F432132BDFB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ A1C44362B8606DEA4742800A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ A87E62B5834E483BD7947E1F /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
+ C7AA65BE03122A7CE76B0214 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ CBE43B9703A2C6829E30C67E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ FDC70B5307628862A4C1C6D0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 331C80D2294CF70F00263BE5 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C5DD5E3E437B30F2BB9B6CBD /* Pods_RunnerTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 86598D225E2B3C9C652FB981 /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 331C80D6294CF71000263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 331C80D6294CF71000263BE5 /* RunnerTests */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ 3FE533F525665BFD9FA8A471 /* Pods */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* mibook.app */,
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 3FE533F525665BFD9FA8A471 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ CBE43B9703A2C6829E30C67E /* Pods-Runner.debug.xcconfig */,
+ FDC70B5307628862A4C1C6D0 /* Pods-Runner.release.xcconfig */,
+ C7AA65BE03122A7CE76B0214 /* Pods-Runner.profile.xcconfig */,
+ 61747C17440559CA3799386A /* Pods-RunnerTests.debug.xcconfig */,
+ 9F8C8FE9A8D09F432132BDFB /* Pods-RunnerTests.release.xcconfig */,
+ A87E62B5834E483BD7947E1F /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ A1C44362B8606DEA4742800A /* Pods_Runner.framework */,
+ 7A51812E6CA24747E4C5F14F /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 331C80D4294CF70F00263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 7512FB06EA385FF9B3AB8DB2 /* [CP] Check Pods Manifest.lock */,
+ 331C80D1294CF70F00263BE5 /* Sources */,
+ 331C80D2294CF70F00263BE5 /* Frameworks */,
+ 331C80D3294CF70F00263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ DC4E08B37EDF9B9FDC1FECDD /* [CP] Check Pods Manifest.lock */,
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ ECC94004D71F30E414DB5311 /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* mibook.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 331C80D4294CF70F00263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 33CC10EC2044A3C60003C045;
+ };
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 331C80D4294CF70F00263BE5 /* RunnerTests */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 331C80D3294CF70F00263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+ 7512FB06EA385FF9B3AB8DB2 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ DC4E08B37EDF9B9FDC1FECDD /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ ECC94004D71F30E414DB5311 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C80D1294CF70F00263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC10EC2044A3C60003C045 /* Runner */;
+ targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
+ };
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 331C80DB294CF71000263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 61747C17440559CA3799386A /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.mibook.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mibook.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mibook";
+ };
+ name = Debug;
+ };
+ 331C80DC294CF71000263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9F8C8FE9A8D09F432132BDFB /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.mibook.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mibook.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mibook";
+ };
+ name = Release;
+ };
+ 331C80DD294CF71000263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = A87E62B5834E483BD7947E1F /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.mibook.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mibook.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mibook";
+ };
+ name = Profile;
+ };
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C80DB294CF71000263BE5 /* Debug */,
+ 331C80DC294CF71000263BE5 /* Release */,
+ 331C80DD294CF71000263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/pubspec.lock b/pubspec.lock
index eaa4bf2..f970ac6 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -57,6 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "9.3.1"
+ auto_size_text:
+ dependency: "direct main"
+ description:
+ name: auto_size_text
+ sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.0"
bloc:
dependency: transitive
description:
@@ -500,26 +508,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
- sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
+ sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
url: "https://pub.dev"
source: hosted
- version: "10.0.9"
+ version: "11.0.1"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
- sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
+ sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
- version: "3.0.9"
+ version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
- sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
- version: "3.0.1"
+ version: "3.0.2"
lints:
dependency: transitive
description:
@@ -753,7 +761,7 @@ packages:
source: hosted
version: "0.28.0"
shared_preferences:
- dependency: transitive
+ dependency: "direct main"
description:
name: shared_preferences
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
@@ -993,26 +1001,26 @@ packages:
dependency: "direct dev"
description:
name: test
- sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
+ sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb"
url: "https://pub.dev"
source: hosted
- version: "1.25.15"
+ version: "1.26.2"
test_api:
dependency: transitive
description:
name: test_api
- sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
+ sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.dev"
source: hosted
- version: "0.7.4"
+ version: "0.7.6"
test_core:
dependency: transitive
description:
name: test_core
- sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
+ sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a"
url: "https://pub.dev"
source: hosted
- version: "0.6.8"
+ version: "0.6.11"
timing:
dependency: transitive
description:
@@ -1041,10 +1049,10 @@ packages:
dependency: transitive
description:
name: vector_math
- sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
- version: "2.1.4"
+ version: "2.2.0"
vm_service:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 8c40f00..3b33d7d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -30,6 +30,8 @@ dependencies:
mockito: ^5.4.6
shimmer: ^3.0.0
flutter_html: ^3.0.0
+ shared_preferences: ^2.5.3
+ auto_size_text: ^3.0.0
dev_dependencies:
flutter_test:
diff --git a/test/layers/data/mocks/mock_encrypted_shared_preferences.dart b/test/layers/data/mocks/mock_encrypted_shared_preferences.dart
new file mode 100644
index 0000000..88068db
--- /dev/null
+++ b/test/layers/data/mocks/mock_encrypted_shared_preferences.dart
@@ -0,0 +1,5 @@
+import 'package:encrypted_shared_preferences/encrypted_shared_preferences.dart';
+import 'package:mockito/mockito.dart';
+
+class MockEncryptedSharedPreferences extends Mock
+ implements EncryptedSharedPreferences {}
diff --git a/test/layers/data/repository/reading_repository_test.dart b/test/layers/data/repository/reading_repository_test.dart
new file mode 100644
index 0000000..8fc0e0a
--- /dev/null
+++ b/test/layers/data/repository/reading_repository_test.dart
@@ -0,0 +1,60 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mibook/layers/data/datasource/reading_data_source.dart';
+import 'package:mibook/layers/data/models/reading_data.dart';
+import 'package:mibook/layers/data/repository/reading_repository.dart';
+import 'package:mibook/layers/domain/models/reading_domain.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+
+import 'reading_repository_test.mocks.dart';
+
+@GenerateNiceMocks([MockSpec()])
+void main() {
+ late MockIReadingDataSource mockIReadingDataSource;
+ late ReadingRepository sut;
+
+ setUp() {
+ mockIReadingDataSource = MockIReadingDataSource();
+ sut = ReadingRepository(mockIReadingDataSource);
+ }
+
+ group('test ReadingRepository', () {
+ setUp();
+
+ test('startReading', () async {
+ final readingDomain = ReadingDomain(bookId: 'id1', progress: 0.5);
+ final readingData = ReadingData(bookId: 'id1', progress: 0.5);
+
+ await sut.startReading(reading: readingDomain);
+
+ verify(
+ mockIReadingDataSource.startReading(
+ readingData: argThat(
+ predicate(
+ (data) =>
+ data.bookId == readingData.bookId &&
+ data.progress == readingData.progress,
+ ),
+ named: 'readingData',
+ ),
+ ),
+ ).called(1);
+ });
+ });
+
+ test('getReadings', () async {
+ final fakeData = [
+ ReadingData(bookId: 'id1', progress: 0.5),
+ ReadingData(bookId: 'id2', progress: 0.5),
+ ];
+
+ when(
+ mockIReadingDataSource.getReadingData(),
+ ).thenAnswer((_) async => fakeData);
+
+ final response = await sut.getReadings();
+
+ verify(mockIReadingDataSource.getReadingData()).called(1);
+ expect(response.length, 2);
+ });
+}
diff --git a/test/layers/domain/fakes/fake_book_domain.dart b/test/layers/domain/fakes/fake_book_domain.dart
index e7aa8a9..a5fad09 100644
--- a/test/layers/domain/fakes/fake_book_domain.dart
+++ b/test/layers/domain/fakes/fake_book_domain.dart
@@ -7,4 +7,5 @@ final fakeBookDomain = BookDomain(
thumbnail: 'thumbnail',
authors: [],
kind: 'kind',
+ pageCount: 0,
);
diff --git a/test/layers/domain/start_reading_test.dart b/test/layers/domain/start_reading_test.dart
new file mode 100644
index 0000000..11c7848
--- /dev/null
+++ b/test/layers/domain/start_reading_test.dart
@@ -0,0 +1,41 @@
+@GenerateNiceMocks([MockSpec()])
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mibook/layers/domain/models/reading_domain.dart';
+import 'package:mibook/layers/domain/repository/reading_repository.dart';
+import 'package:mibook/layers/domain/usecases/start_reading.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+
+import 'start_reading_test.mocks.dart';
+
+void main() {
+ late MockIReadingRepository mockIReadingRepository;
+ late StartReading sut;
+
+ setUp() {
+ mockIReadingRepository = MockIReadingRepository();
+ sut = StartReading(mockIReadingRepository);
+ }
+
+ group('test StartReading', () {
+ setUp();
+
+ test('start', () async {
+ final readingDomain = ReadingDomain(bookId: 'id1', progress: 0.5);
+ // when(
+ // mockIReadingRepository.startReading(reading: readingDomain),
+ // ).thenAnswer((_) async => Future.value);
+ await sut(reading: readingDomain);
+ verify(
+ mockIReadingRepository.startReading(
+ reading: argThat(
+ predicate(
+ (data) => data.bookId == 'id1' && data.progress == 0.5,
+ ),
+ named: 'reading',
+ ),
+ ),
+ ).called(1);
+ });
+ });
+}
diff --git a/test/layers/presentation/viewmodel/start_reading_view_model_test.dart b/test/layers/presentation/viewmodel/start_reading_view_model_test.dart
new file mode 100644
index 0000000..1e304af
--- /dev/null
+++ b/test/layers/presentation/viewmodel/start_reading_view_model_test.dart
@@ -0,0 +1,116 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mibook/core/utils/strings.dart';
+import 'package:mibook/layers/domain/usecases/start_reading.dart';
+import 'package:mibook/layers/presentation/screens/bookdetails/book_details_ui.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_event.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_state.dart';
+import 'package:mibook/layers/presentation/screens/startreading/start_reading_view_model.dart';
+import 'package:mockito/annotations.dart';
+
+@GenerateNiceMocks([MockSpec()])
+import 'start_reading_view_model_test.mocks.dart';
+
+void main() {
+ late MockIStartReading startReading;
+ late StartReadingViewModel sut;
+ final fakeBook = BookDetailsUI(
+ id: 'id',
+ kind: 'kind',
+ title: 'title',
+ authors: 'authors',
+ description: 'description',
+ thumbnail: 'thumbnail',
+ pageCount: 100,
+ );
+
+ setUp() {
+ startReading = MockIStartReading();
+ sut = StartReadingViewModel(startReading, fakeBook);
+ }
+
+ group('test StartReadingViewModel', () {
+ setUp();
+ test(
+ 'given progress > 1.0, when add didEditProgressEvent, should state have input error message',
+ () async {
+ sut.add(DidEditProgressEvent(progress: 200));
+
+ await expectLater(
+ sut.stream,
+ emits(
+ predicate(
+ (state) =>
+ state.inputErrorMessage == progressErrorMessage &&
+ state.progress == 0.0,
+ ),
+ ),
+ );
+ },
+ );
+
+ test(
+ 'given progress <= 1.0, when add didEditProgressEvent, should state have progress',
+ () async {
+ sut.add(DidEditProgressEvent(progress: 50));
+
+ await expectLater(
+ sut.stream,
+ emits(
+ predicate(
+ (state) =>
+ state.inputErrorMessage == null && state.progress == 0.5,
+ ),
+ ),
+ );
+ },
+ );
+
+ test(
+ 'didClickConfirmEvent',
+ () async {
+ sut.emit(
+ StartReadingState(
+ inputErrorMessage: null,
+ progress: 0.5,
+ shouldNavigateBack: false,
+ ),
+ );
+
+ sut.add(DidClickConfirmEvent());
+
+ await expectLater(
+ sut.stream,
+ emits(
+ predicate(
+ (state) => state.shouldNavigateBack,
+ ),
+ ),
+ );
+ },
+ );
+
+ test(
+ 'didFinishBookEvent',
+ () async {
+ sut.emit(
+ StartReadingState(
+ inputErrorMessage: null,
+ progress: 0.5,
+ shouldNavigateBack: false,
+ ),
+ );
+
+ sut.add(DidClickFinishBookEvent());
+
+ await expectLater(
+ sut.stream,
+ emits(
+ predicate(
+ (state) => state.shouldNavigateBack,
+ ),
+ ),
+ );
+ },
+ );
+ });
+}