diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..75a988d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI +on: push +jobs: + flutter-ci: + name: run tests + runs-on: ubuntu-latest + # see https://github.com/marketplace/actions/flutter-action + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '12.x' + - uses: subosito/flutter-action@v1 + with: + flutter-version: '1.22.x' + channel: 'stable' + - run: flutter pub get + - run: flutter analyze + - run: sh ./coverage_script.bash todolist + - run: flutter test --coverage + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage/lcov.info \ No newline at end of file diff --git a/.gitignore b/.gitignore index dbef116..e43854e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ doc/api/ *.js_ *.js.deps *.js.map +coverage/ +.flutter-plugins-dependencies +lib/generated_plugin_registrant.dart +# test file for coverage +test/coverage_helper_test.dart +.flutter_plugins \ No newline at end of file diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml new file mode 100644 index 0000000..fd8fbb6 --- /dev/null +++ b/.idea/libraries/Dart_SDK.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/KotlinJavaRuntime.xml b/.idea/libraries/KotlinJavaRuntime.xml new file mode 100644 index 0000000..2b96ac4 --- /dev/null +++ b/.idea/libraries/KotlinJavaRuntime.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..42cd1fd --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/runConfigurations/main_dart.xml b/.idea/runConfigurations/main_dart.xml new file mode 100644 index 0000000..aab7b5c --- /dev/null +++ b/.idea/runConfigurations/main_dart.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..5b3388c --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..911b6b7 --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 81a45ec2e5f80fa71d5135f1702ce540558b416d + channel: beta + +project_type: app diff --git a/README.md b/README.md index fa65111..8d636be 100644 --- a/README.md +++ b/README.md @@ -1,466 +1,102 @@ -
- -# Todo List Flutter Tutorial - -A **_step-by-step_ tutorial** showing how to -build a **Todo List App _from scratch_** in **Flutter**. - -![Screen Shot 2020-01-15 at 11 04 33](https://user-images.githubusercontent.com/27420533/72429017-d8c29e80-3786-11ea-9682-303989093526.png) +# Flutter TodoList Tutorial +Create a simple todolist Flutter application +
+
-## Why? - -This application aims to test knowledge acquired with Flutter through an example of an application that can be used in real life (day-to-day). - -Apps developed using Flutter are faster compared to other multi-platform frameworks.
-Because they have their own widgets, applications in Flutter have a lightweight interface that allows a great user experience.
- -Unlike its main competitor (React Native), Flutter has a very simple and practical installation and configuration setup, even on -Linux.
-With few commands (Flutter doctor, for example), you can check for problems and how to fix them. Some IDEs, like Intellij IDEA, Android Studio and VS Code, have integration with Dart/Flutter through plugins, making the experience more advantageous to the developer. - -## What? - -Let's create a fully functional Todo List through Flutter.
-During this tutorial we will achieve: - -+ [x] Create the design for a day-to-day Application -+ [x] Create another folder and pages within the project -+ [x] How to show the various pages (Events/Tasks) -+ [x] Creating an Insert Page -+ [x] Working with page transitions -+ [x] How to work with the Flutter MOOR database in order to save all events and tasks already performed - -### Todo List? - -At their most basic, to-dos contain all of the tasks that you need to complete on a given day.
It’s a great device for managing time that enables you to lay out everything that you need to accomplish and plan and prioritize your day from there.
You can also make to-dos for major tasks like a work assignment or an overall goal. - - -### Why learn to use them? - -The main purpose of a to-do is to help organize tasks, so learning to effectively use them will benefit you by improving your time management abilities and decreasing your stress levels.
-A to-do allows you to better manage your time by allowing you to lay out what you need to accomplish and then coordinate your time from there. When you first compose it, you’ll note the most important tasks and make time for them. - -## Who? - -This tutorial is for all people looking to develop their skills with Flutter while building a day-to-day application.
-For all people looking to improve the organization of time. - -### Prerequisites to this Tutorial - -Everyone looking to learn more about Flutter should be able to perform this application without problems.
-If you get stuck in one step the best thing to do is to open an issue because besides us being able to answer quickly , other people who have the same doubt can be helped.
-If you need to open an issue here is the link to open that issue: https://github.com/dwyl/flutter-todo-list-tutorial/issues - -## MOOR - -Moor is an easy to use, reactive persistence library for Flutter apps. Define your database tables in pure Dart and enjoy a fluent query API, auto-updating streams and more! - -### MOOR advantages: - -- **Flexible:** Moor let's you write queries in both SQL and Dart, providing fluent apis for both languages. You can filter and order results or use joins to run queries on multiple tables. -- **Feature rich:** Moor has builtin support for transactions, schema migrations, complex filters and expressions, batched updates and joins. -- **Modular:** Thanks to builtin support for daos and imports in sql files, moor helps you keep your database code simple. -- **Safe:** Moor generates typesafe code based on your tables and queries. If you make a mistake in your queries, moor will find it at compile time and provide helpful and descriptive lints. -- **Fast:** Even though moor lets you write powerful queries, it can keep up with the performance of key-value stores like shared preferences and Hive. -- **Cross-Platform support:** Moor works on Android, iOS, macOS, Windows, Linux and the web. - -If you want to start using MOOR here is the link : https://moor.simonbinder.eu/docs/getting-started/ - -## _How?_ - -### Pre-requisites (Before you Start) -1. **Flutter Installation**
- see: https://github.com/dwyl/learn-flutter
- > _**Note**: if you already have `Flutter ` installed on your Mac, - and just want to upgrade to the latest version, run:_ - **`flutter upgrade`** - -2.**Android Studio** or **Visual Studio Code**
-See:https://developer.android.com/studio or https://code.visualstudio.com/download
+To run the project on your machine: +- make sure you have Dart and Flutter installed: https://flutter.dev/docs/get-started/install +- clone this repository with `git clone git@github.com:dwyl/flutter-todo-list-tutorial.git` +- Before running the application check that all the tests are passing with `flutter test` +The application contains the following main folders: +- `lib/models` Contains the `task` and `todolist` models +- `lib/screens` Contains the different screens of the application. Currently only the tasks screen exists. + In the tasks folder you can find the task, tasks and todolist widgets which define the UI of this screen. +- `lib/widgets` This folder is used to store widgets used on multiple screen, currently this folder is empty +- `test` This contains the unit, widget and integration tests. - 1-Create a new Flutter Project. - -![image](https://user-images.githubusercontent.com/27420533/72510378-3f0bf780-3841-11ea-840d-6d56f8663357.png) - - -2-Delete all the "main.dart" comments that are generated after creating a new project. - - -### Design - -3- Create a new "bottomNavigationBar" and inside that "bottomNavigationBar" create a "Row" to save the buttons to add new tasks/events and the settings button. - -```ruby - bottomNavigationBar: BottomAppBar( - shape: CircularNotchedRectangle(), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - IconButton( - icon: Icon(Icons.settings), - onPressed: () {}, - ), - IconButton( - icon: Icon(Icons.more_vert), - onPressed: () {}, -``` -4- Inside the row add 2 buttons. - -5-Use the function "FloatingActionButtonLocation" to center the add button. - -```ruby -child: Icon(Icons.add), - ), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - -``` -6-Inside the body add a Column to store the day of the week. - -```ruby - child: Text( - "Monday", - style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold), - ), - -``` -7- Create a "CustomButton" to get the tasks and another one for the events. - -```ruby -child: CustomButton( - onPressed: () { - _pageController.previousPage( - duration: Duration(milliseconds: 500), - curve: Curves.bounceInOut); - }, - buttonText: "Tasks", - color: - currentPage < 0.5 ? Theme.of(context).accentColor : Colors.white, - textColor: - currentPage < 0.5 ? Colors.white : Theme.of(context).accentColor, - borderColor: currentPage < 0.5 - ? Colors.transparent - : Theme.of(context).accentColor, - )), - -``` - -8- Inside that "Row" you will have a children.Then it is necessary to add two "IconButton". - - -```ruby -child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - IconButton( - icon: Icon(Icons.settings), - onPressed: () {}, - ), - IconButton( - icon: Icon(Icons.more_vert), - onPressed: () {}, - ) -``` -9- To add the Shape of the footer before Row, it is necessary to add the "shape" function and look for "CircularNotchedRectangle". - - -```ruby - bottomNavigationBar: BottomAppBar( - shape: CircularNotchedRectangle(), - child: Row( -``` - -This will be the final result of our bar. +The application is using [Provider](https://pub.dev/packages/provider) to manage the states. +Providers allow us to notify the UI of the application when a state change. For example when a +task is toggled the `notifyListener` function is called which let the application knows that a refresh of the +task's UI is required: -![Screen Shot 2020-01-16 at 09 44 31](https://user-images.githubusercontent.com/27420533/72513312-dc1c5f80-3844-11ea-9766-b280988969c1.png) - - - -10- To write the Application Name we go to the _MainContent class and inside the return Scaffold we add a SizedBox, a child and then we add a Text with what we want to write. - -```ruby - SizedBox(height: 60), - Padding( - padding: const EdgeInsets.all(24.0), - child: Text( - "Todo List ", -``` - -11- Now we have to create the buttons one for the tasks and one for the events. -For that right after the Text we open another Row.
- -Inside this Row we will have a child then we add a "MaterialButton" with a child who takes a Text with the text that appears on the button. - - - -```ruby -return Row( - children: [ - Expanded( - child: CustomButton( - onPressed: () { - _pageController.previousPage( - duration: Duration(milliseconds: 500), - curve: Curves.bounceInOut); - }, - buttonText: "Tasks", - -``` -This will be the final layout of the two buttons and our title. - - -![Screen Shot 2020-01-16 at 11 45 50](https://user-images.githubusercontent.com/27420533/72522520-d24f2800-3855-11ea-830e-2155907ed51f.png) - -12- To create the tasks we must use Radio Buttons that will be placed under the name of the application.
-It will take the function "radio_button_unchecked".
-Tasks can be added through "_taskUncomplete". - -13- Create two new widgets one for tasks already done and one for tasks that have not yet been done.
- -The two will take radio buttons and the one for the tasks already done takes checked radio buttons and the one for the tasks not yet done takes unchecked radio buttons. - -![Screen Shot 2020-01-18 at 11 44 40](https://user-images.githubusercontent.com/27420533/72663220-fd698100-39e7-11ea-884a-fca5b13e78da.png) - - -![Screen Shot 2020-01-18 at 11 43 56](https://user-images.githubusercontent.com/27420533/72663223-0d816080-39e8-11ea-9c8f-9c1a4666c5d2.png) - - -## Task/Event Page - -1- First hide the debug banner. - - -```ruby -child: MaterialApp( - debugShowCheckedModeBanner: false, - title: 'Flutter Demo', - +```dart + void toggle() { + completed = !completed; + notifyListeners(); + } ``` -2- Now we'll have to create another package that will contain the events and tasks page separately. - -![Screen Shot 2020-01-18 at 10 49 58](https://user-images.githubusercontent.com/27420533/72662537-5d5c2980-39e0-11ea-84cd-1a54bdd15b53.png) -### How to Create/Add new Package inside src folder +## Flutter concepts -1- Open Android Studio and Navigate to any view(Android or Project)
-2- In Android View you will have two folders: app and Gradle Scripts
-3- Open App folder then open Java folder. Right click on Java folder and select New > Package.
+### Widgets -![newpackage](https://user-images.githubusercontent.com/27420533/72662715-546c5780-39e2-11ea-8eff-6118fcd0c339.jpeg) +Flutter is using Widgets to create the applications' UI. +Widgets let you declare how and what to render on the screen. +Widgets can be composed to create more complex UI, creating a widgets tree, +similar to the [Document Object Model](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) +which is used to represent html pages. -4- Choose directory destination which main\java and click OK. +For example a todo list app could be represented with the follownig wireframe: -![Screen Shot 2020-01-18 at 11 07 54](https://user-images.githubusercontent.com/27420533/72662780-2fc4af80-39e3-11ea-8ac0-043ca3e7be6b.png) +![todolist-app](https://user-images.githubusercontent.com/6057298/93343915-f3bf4400-f828-11ea-9087-d7cac865cecd.png) +From there we can start to have an idea of the widgets tree structure: -5- Give a name to new Package(For example: pages). Click Ok. +![widgets-tree](https://user-images.githubusercontent.com/6057298/93343977-03d72380-f829-11ea-8c4b-dc964c591e97.png) -![Screen Shot 2020-01-18 at 11 13 59](https://user-images.githubusercontent.com/27420533/72662829-b5e0f600-39e3-11ea-9f27-95c2d9d4acfe.png) - -3- After creating the new package. Right click on the package and Select New > Dart File.
-Name the New Dart File > task_page.dart.
+see also: -4- Create another one called event_page.dart.
+- Introduction to widgets: +- Widget catalog: +- widget index: +- widget of the week on Youtube: -5- Inside the taks_page.dart create a new StatefulWidget, and import your material.dart.
+### Create a new Flutter application -![Screen Shot 2020-01-18 at 11 32 31](https://user-images.githubusercontent.com/27420533/72663097-589a7400-39e6-11ea-84bf-b6c4711ec0b3.png) +#### A quick CLI tour -![Screen Shot 2020-01-18 at 11 32 14](https://user-images.githubusercontent.com/27420533/72663101-66e89000-39e6-11ea-9f3c-57e2d7444c92.png) +You can create a new Flutter project with the following command line: -6- Move all the related task code to task widget. - -7- Display task widgets in main content. - -![Screen Shot 2020-01-18 at 12 29 01](https://user-images.githubusercontent.com/27420533/72663781-2d1b8780-39ee-11ea-9869-89f31c47eee7.png) - - -8- Don't Forget to initiate itemCount in ListView. - - -![Screen Shot 2020-01-18 at 12 32 09](https://user-images.githubusercontent.com/27420533/72663829-b59a2800-39ee-11ea-8b3d-96458d521060.png) - - -### Event Page - -1- Create the StatefulWidget in the event_page.dart and import the material.dart package. - -2- Now in the _EventPageState change the return Column to return Listview.builder. - -```ruby -class _EventPageState extends State { - @override - Widget build(BuildContext context) { - double iconSize = 20; - - return ListView.builder( - itemCount: _eventList.length, - padding: const EdgeInsets.all(0), - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.only(left: 24.0, right: 24), - - -``` -3- We have icon, time and description,so we wrap all to Row. - - - -```ruby - - child: Row( - children: [ - _lineStyle(context, iconSize, index, _eventList.length, - _eventList[index].isFinish), - _displayTime(_eventList[index].time), - _displayContent(_eventList[index]) - - +```sh +flutter create --org com.dwyl --project-name todolist . ``` -4- For description we wrap in Column because it have 2 lines. - - -![Screen Shot 2020-01-18 at 13 14 34](https://user-images.githubusercontent.com/27420533/72664334-871f4b80-39f4-11ea-955e-c2ebe1efbc97.png) - -5- Now it's time to draw the line. - - -```ruby -Widget _lineStyle(BuildContext context, double iconSize, int index, - int listLength, bool isFinish) { - return Container( - decoration: CustomIconDecoration( - iconSize: iconSize, - lineWidth: 1, - firstData: index == 0 ?? true, - lastData: index == listLength - 1 ?? true), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(50)), - boxShadow: [ - BoxShadow( - offset: Offset(0, 3), - color: Color(0x20000000), - blurRadius: 5) - ]), - - ``` - 6- Define the position for Draw Line. - - -```ruby - void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { - final leftOffset = Offset((iconSize / 2) + 24, offset.dy); - final double iconSpace = iconSize / 1.5; - - final Offset top = configuration.size.topLeft(Offset(leftOffset.dx, 0.0)); - final Offset centerTop = configuration.size - .centerLeft(Offset(leftOffset.dx, leftOffset.dy - iconSpace)); - - final Offset centerBottom = configuration.size - .centerLeft(Offset(leftOffset.dx, leftOffset.dy + iconSpace)); - final Offset end = - configuration.size.bottomLeft(Offset(leftOffset.dx, leftOffset.dy * 2)); - - if (!firstData) canvas.drawLine(top, centerTop, paintLine); - if (!lastData) canvas.drawLine(centerBottom, end, paintLine); - } -} - - ``` - -7- Define value for Icon Size, and assign value for firstData and LastData. +This will create the project `todolist` in the current folder `.`. +The `--org` flag uses the [reverse domain name notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation) to identify your application. +You can then run the application with `flutter run` and run the tests with `flutter test`. -```ruby -return Container( - decoration: CustomIconDecoration( - iconSize: iconSize, - lineWidth: 1, - firstData: index == 0 ?? true, - lastData: index == listLength - 1 ?? true), - - ``` -8- Create the Object for Event. +For the list of command type `flutter help`. +For more details about a specific command, for example `create`, run `flutter create --help` -```ruby -class Event { - final String time; - final String task; - final String desc; - final bool isFinish; +#### Material Design - const Event(this.time, this.task, this.desc, this.isFinish); -} +[Material Design](https://material.io/design/introduction) is a guideline to create user interface. +Flutter implements the guideline with the [material components widgets](https://flutter.dev/docs/development/ui/widgets/material). +This list of widgest allow us to create rapdly a UI folling the best practices from material design. +To use these widgets you need first to import the `material` Dart package with `import 'package:flutter/material.dart';` +You can then browse all the material widgets and select the ones required for your application -final List _eventList = [ - new Event("08:00", "Have coffe with Sam", "Personal", true), - new Event("10:00", "Meet with sales", "Work", true), - new Event("12:00", "Call Tom about appointment", "Work", false), - new Event("14:00", "Fix onboarding experience", "Work", false), - new Event("16:00", "Edit API documentation", "Personal", false), - new Event("18:00", "Setup user focus group", "Personal", false), -]; +You have also the possiblity to create an IOs look by using the [Cupertino widgets package](https://flutter.dev/docs/development/ui/widgets/cupertino) - - ``` - -## Insert Page +#### Main Widgets used -1- Create a new Dart file called "add_task_page.dart",import your material.dart and create a new StatefulWidget.
-After that change the Return Container to Return Text and add the Text "Add New Task".
+- [Scaffold](https://api.flutter.dev/flutter/material/Scaffold-class.html) +- [AppBar](https://api.flutter.dev/flutter/material/AppBar-class.html) +- [CheckboxListTile](https://api.flutter.dev/flutter/material/CheckboxListTile-class.html) +- [ListView](https://api.flutter.dev/flutter/widgets/ListView-class.html) +- [TextField](https://api.flutter.dev/flutter/material/TextField-class.html) +- [Column](https://api.flutter.dev/flutter/widgets/Column-class.html) +- [Expanded](https://api.flutter.dev/flutter/widgets/Expanded-class.html) -```ruby - mainAxisSize: MainAxisSize.min, - children: [ - Center( - child: Text( - "Add new task", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), - )), - - - ``` - -2- Create a CustomTextField that contais a labelText with the Text "Enter task Name". - -```ruby - CustomTextField( - labelText: 'Enter task name', controller: _textTaskControler), - SizedBox(height: 12), - - ``` - - 3- Create a new Package Called Widgets , inside this package is where all the elements of interaction that we will use in our application will be. - - - ![Screen Shot 2020-01-18 at 21 58 08](https://user-images.githubusercontent.com/27420533/72671141-be661a80-3a3d-11ea-849d-93fb89063ed8.png) - - - 4- Create a Dart file called Custom_button.dart, inside that button add the StatelessWidget and import the material.dart.
- Create the CustomButton Object.
- -```ruby -import 'package:flutter/material.dart'; +Note that the `Column` and `Exapanded` widgets are "space" widgets. -class CustomButton extends StatelessWidget { - final VoidCallback onPressed; - final String buttonText; - final Color color; - final Color textColor; - final Color borderColor; +Flutter provide a widget inspector where you can see the full tree +of the application: - - ``` - - - - - - - +![widget tree](https://user-images.githubusercontent.com/6057298/93480078-f6876b00-f8f4-11ea-95df-3c81321e8284.png) \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..0a741cb --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..58321ec --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,63 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 28 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.dwyl.todolist" + minSdkVersion 16 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..6ca8e9c --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..11d3bcb --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/dwyl/todolist/MainActivity.kt b/android/app/src/main/kotlin/com/dwyl/todolist/MainActivity.kt new file mode 100644 index 0000000..f37f193 --- /dev/null +++ b/android/app/src/main/kotlin/com/dwyl/todolist/MainActivity.kt @@ -0,0 +1,6 @@ +package com.dwyl.todolist + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..1f83a33 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..6ca8e9c --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..3100ad2 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..38c8d45 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..296b146 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/android/todolist_android.iml b/android/todolist_android.iml new file mode 100644 index 0000000..1029d72 --- /dev/null +++ b/android/todolist_android.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/coverage_script.bash b/coverage_script.bash new file mode 100644 index 0000000..a15d0e1 --- /dev/null +++ b/coverage_script.bash @@ -0,0 +1,6 @@ +#!/bin/sh +file=test/coverage_helper_test.dart +echo "// Helper file to make coverage work for all dart files\n" > $file +echo "// ignore_for_file: unused_import" >> $file +find lib -not -name '*.g.dart' -and -name '*.dart' -and -not -name 'generated_*.dart' -and -not -name 'main.dart' | cut -c4- | awk -v package=$1 '{printf "import '\''package:%s%s'\'';\n", package, $1}' >> $file +echo "void main(){}" >> $file \ No newline at end of file diff --git a/integration_test/todolist_test.dart b/integration_test/todolist_test.dart new file mode 100644 index 0000000..7808bc2 --- /dev/null +++ b/integration_test/todolist_test.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:todolist/screens/tasks/tasks.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets("failing test example", (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp(title: 'TodoList', home: Tasks())); + expect(2 + 2, equals(4)); + + final textField = find.byType(TextField); + expect(textField, findsOneWidget); + + // enter text in field + await tester.enterText(textField, 'task 1'); + final textFieldWidget = tester.widget(textField) as TextField; + textFieldWidget.onSubmitted(textFieldWidget.controller.value.text); + await tester.pumpAndSettle(); + + // find new task created + expect(find.byType(CheckboxListTile), findsOneWidget); + expect(find.text('task 1'), findsOneWidget); + }); +} \ No newline at end of file diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..e96ef60 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..f2872cf --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b098a1f --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,495 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.dwyl.todolist; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.dwyl.todolist; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.dwyl.todolist; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..a28140c --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..85a3ec8 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + todolist + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..6cabc95 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; +import 'package:todolist/screens/tasks/tasks.dart'; + +void main() { + return runApp( + MaterialApp(title: 'TodoList', home: Tasks()) + ); +} \ No newline at end of file diff --git a/lib/models/task.dart b/lib/models/task.dart new file mode 100644 index 0000000..ad656d9 --- /dev/null +++ b/lib/models/task.dart @@ -0,0 +1,21 @@ +import 'package:flutter/foundation.dart'; + +/// Task model +/// A task contains a string text and a status completed. +class TaskModel extends ChangeNotifier { + final String text; + bool completed; + + TaskModel({this.text, this.completed = false}); + + TaskModel.fromJson(Map json) + : text = json['text'], + completed = json['completed']; + + Map toJson() => {'text': text, 'completed': completed}; + + void toggle() { + completed = !completed; + notifyListeners(); + } +} diff --git a/lib/models/todoList.dart b/lib/models/todoList.dart new file mode 100644 index 0000000..9a6a474 --- /dev/null +++ b/lib/models/todoList.dart @@ -0,0 +1,28 @@ +import 'package:flutter/foundation.dart'; +import 'package:todolist/models/task.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'dart:convert'; + +class TodoListModel extends ChangeNotifier { + List tasks = []; + + void addTaks(TaskModel task) { + tasks.add(task); + notifyListeners(); + } + + Future getTasksFromSharedPrefs() async { + final prefs = await SharedPreferences.getInstance(); + final tasksJson = prefs.getString('tasks') ?? '[]'; + // https://flutter.dev/docs/cookbook/networking/background-parsing#convert-the-response-into-a-list-of-photos + final jsonListTasks = jsonDecode(tasksJson).cast>(); + tasks = jsonListTasks.map((m) => TaskModel.fromJson(m)).toList(); + notifyListeners(); + } + + Future saveTasksToSharedPrefs() async { + final prefs = await SharedPreferences.getInstance(); + final json = jsonEncode(tasks); + prefs.setString('tasks', json); + } +} diff --git a/lib/screens/tasks/task.dart b/lib/screens/tasks/task.dart new file mode 100644 index 0000000..51266d8 --- /dev/null +++ b/lib/screens/tasks/task.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:todolist/models/task.dart'; +import 'package:provider/provider.dart'; +import 'package:todolist/models/todoList.dart'; + +class TaskWidget extends StatelessWidget { + // method to style completed/uncompleted item + TextStyle _taskStyle(completed) { + if (completed) + return TextStyle( + color: Colors.black54, + decoration: TextDecoration.lineThrough, + ); + else + return TextStyle(decoration: TextDecoration.none); + } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, task, child) { + return CheckboxListTile( + title: Text( + task.text, + style: _taskStyle(task.completed), + ), + value: task.completed, + onChanged: (newValue) { + task.toggle(); + Provider.of(context, listen: false) + .saveTasksToSharedPrefs(); + }, + controlAffinity: ListTileControlAffinity.leading, + ); + }); + } +} diff --git a/lib/screens/tasks/tasks.dart b/lib/screens/tasks/tasks.dart new file mode 100644 index 0000000..3f04d36 --- /dev/null +++ b/lib/screens/tasks/tasks.dart @@ -0,0 +1,20 @@ +import 'package:provider/provider.dart'; +import 'package:todolist/models/todoList.dart'; +import 'package:todolist/screens/tasks/todolist.dart'; +import 'package:flutter/material.dart'; + +class Tasks extends StatelessWidget { + @override + Widget build(BuildContext context) { + final TodoListModel todoList = TodoListModel(); + todoList.getTasksFromSharedPrefs(); + return Scaffold( + appBar: AppBar( + title: Text('TodoList'), + ), + body: ChangeNotifierProvider.value( + value: todoList, + child: TodoListWidget() ,) + ); + } +} \ No newline at end of file diff --git a/lib/screens/tasks/todolist.dart b/lib/screens/tasks/todolist.dart new file mode 100644 index 0000000..fc41271 --- /dev/null +++ b/lib/screens/tasks/todolist.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:todolist/models/task.dart'; +import 'package:todolist/models/todoList.dart'; +import 'package:todolist/screens/tasks/task.dart'; +import 'package:provider/provider.dart'; + +class TodoListWidget extends StatelessWidget { + final TextEditingController _controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Column(children: [ + Expanded(child: Consumer(builder: (context, tasks, child) { + return ListView( + children: tasks.tasks.map((TaskModel task) { + return ChangeNotifierProvider.value( + value: task, child: TaskWidget()); + }).toList(), + ); + })), + Consumer( + builder: (contexst, tasks, child) { + return TextField( + controller: _controller, + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.teal)), + labelText: 'new task'), + onSubmitted: (newTask) { + tasks.addTaks(TaskModel(text: newTask)); // create new instance of task changeNotifier model + _controller.clear(); // clear the text input after adding taks + tasks.saveTasksToSharedPrefs(); + }, + ); + }, + ) + ]); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..423398b --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,81 @@ +name: todolist +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + shared_preferences: '>=0.5.12 <2.0.0' + provider: ^4.3.2+4 + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^0.1.3 + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: ^1.0.1 + flutter_driver: + sdk: flutter + + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file diff --git a/test/task_test.dart b/test/task_test.dart new file mode 100644 index 0000000..337111f --- /dev/null +++ b/test/task_test.dart @@ -0,0 +1,21 @@ +import 'package:todolist/models/task.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Decode and encode task to json', () { + final json = {'text': 'task #1', 'completed': true}; + final TaskModel t = TaskModel.fromJson(json); + + expect(t.completed, true); + expect(t.text, 'task #1'); + expect(t.toJson(), json); + }); + + test('Toggle task completed/uncompleted', () { + final item = TaskModel(text: 'new item', completed: false); + item.toggle(); + expect(item.completed, true); + item.toggle(); + expect(item.completed, false); + }); +} diff --git a/test/task_widget_test.dart b/test/task_widget_test.dart new file mode 100644 index 0000000..91600f7 --- /dev/null +++ b/test/task_widget_test.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:todolist/models/task.dart'; +import 'package:todolist/models/todoList.dart'; +import 'package:todolist/screens/tasks/task.dart'; +import 'package:provider/provider.dart'; + +void main() { + testWidgets('Task widget', (WidgetTester tester) async { + final task = TaskModel(text: 'task 1'); + await tester.pumpWidget(MaterialApp( + home: MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => TodoListModel()), + ChangeNotifierProvider.value( + value: task, + ) + ], + child: Scaffold( + // create a dummy scaffold to be able to use the TaskWidget + appBar: AppBar( + title: Text('Task Widget Test'), + ), + body: TaskWidget())))); + // find the task 1 text + final textTask = find.text('task 1'); + expect(textTask, findsOneWidget); + + // find the checkboxListTile widget + final checkboxListTile = find.byType(CheckboxListTile); + expect(checkboxListTile, findsOneWidget); + + // check task is not completed + expect(task.completed, false); + + Text textWidgetBeforeTap = tester.widget(textTask) as Text; + expect(textWidgetBeforeTap.style.decoration, TextDecoration.none); + await tester.tap(checkboxListTile); + + // rebuilt widget after tap action + await tester.pumpAndSettle(); + expect(task.completed, true); + Text textWidgetAfterTap = tester.widget(textTask) as Text; + expect(textWidgetAfterTap.style.decoration, TextDecoration.lineThrough); + }); +} diff --git a/test/tasks_widget_test.dart b/test/tasks_widget_test.dart new file mode 100644 index 0000000..250001c --- /dev/null +++ b/test/tasks_widget_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:todolist/models/todoList.dart'; +import 'package:todolist/screens/tasks/tasks.dart'; +import 'package:provider/provider.dart'; + +void main() { + testWidgets('Test Todolist widget', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: Provider( + create: (_) => TodoListModel(), child: Scaffold(body: Tasks())))); + + // Check title exists + final titleFinder = find.text('TodoList'); + expect(titleFinder, findsOneWidget); + + // check input field to create new task exists + final textField = find.byType(TextField); + expect(textField, findsOneWidget); + }); +} diff --git a/test/todoList_test.dart b/test/todoList_test.dart new file mode 100644 index 0000000..3a6670a --- /dev/null +++ b/test/todoList_test.dart @@ -0,0 +1,30 @@ +import 'package:todolist/models/todoList.dart'; +import 'package:todolist/models/task.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Add a task to the todolist', () { + final todoList = TodoListModel(); + expect(todoList.tasks.length, 0); + final task = TaskModel(text: "task 1"); + todoList.addTaks(task); + expect(todoList.tasks.length, 1); + }); + + test('save and get todolist in shared preferences', () async { + // create and save a todolist + final todoList = TodoListModel(); + final task = TaskModel(text: "task 1"); + final task2 = TaskModel(text: "task 2"); + final task3 = TaskModel(text: "task 3"); + todoList.addTaks(task); + todoList.addTaks(task2); + todoList.addTaks(task3); + await todoList.saveTasksToSharedPrefs(); + + // get tasks from shared preferences + final todoList2 = TodoListModel(); + await todoList2.getTasksFromSharedPrefs(); + expect(todoList2.tasks.length, 3); + }); +} diff --git a/test/todolist_widget_test.dart b/test/todolist_widget_test.dart new file mode 100644 index 0000000..8473157 --- /dev/null +++ b/test/todolist_widget_test.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:todolist/models/task.dart'; +import 'package:todolist/models/todoList.dart'; +import 'package:todolist/screens/tasks/todolist.dart'; +import 'package:provider/provider.dart'; + +void main() { + testWidgets('Test Todolist widget', (WidgetTester tester) async { + final todoList = TodoListModel(); + final task = TaskModel(text: "task 1"); + final task2 = TaskModel(text: "task 2"); + final task3 = TaskModel(text: "task 3"); + todoList.addTaks(task); + todoList.addTaks(task2); + todoList.addTaks(task3); + await tester.pumpWidget( + MaterialApp( + home: ChangeNotifierProvider.value( + value: todoList, child: Scaffold(body: TodoListWidget()))), + ); + + final textTask1 = find.text('task 1'); + final textTask2 = find.text('task 2'); + final textTask3 = find.text('task 3'); + expect(textTask1, findsOneWidget); + expect(textTask2, findsOneWidget); + expect(textTask3, findsOneWidget); + }); + + testWidgets('Add a new task', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: ChangeNotifierProvider( + create: (_) => TodoListModel(), + child: Scaffold(body: TodoListWidget()))), + ); + final textField = find.byType(TextField); + expect(textField, findsOneWidget); + + // enter text in field + await tester.enterText(textField, 'task 1'); + final textFieldWidget = tester.widget(textField) as TextField; + textFieldWidget.onSubmitted(textFieldWidget.controller.value.text); + await tester.pumpAndSettle(); + await tester.pumpAndSettle(); + + // find new task created + expect(find.byType(CheckboxListTile), findsOneWidget); + expect(find.text('task 1'), findsOneWidget); + }); +} diff --git a/test_driver/integration_test.dart b/test_driver/integration_test.dart new file mode 100644 index 0000000..6854dea --- /dev/null +++ b/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); \ No newline at end of file diff --git a/todolist.iml b/todolist.iml new file mode 100644 index 0000000..e5c8371 --- /dev/null +++ b/todolist.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..d4aa7f8 --- /dev/null +++ b/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + todolist + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..eb5e43b --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "todolist", + "short_name": "todolist", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +}