diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..11599b2 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1 @@ +exec flutter analyze \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ac04e3a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# Contributing to Splitr + +Thank you for considering contributing to Splitr! :heart: We welcome contributions from the community to help improve and enhance the app. To ensure a smooth and consistent contribution process, please follow the guidelines outlined below. + +If you like the project, but just don't have time to contribute, there are other easy ways to support the project and show your appreciation, which we would also be very happy about: + - Use the application + - Star the project + - Mention the project to your surroundings + +## I have an issue + +Before asking a question or submitting a new bug report, it is best to search for existing [Issues](https://github.com/BhasherBEL/Splitr/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write in this issue. + +If you then still feel the need to ask a question and need clarification, we recommend the following: + +- Make sure that you use the latest version of the application. +- Open an [Issue](https://github.com/BhasherBEL/Splitr/issues/new). +- Provide as much context as you can about what you're running into. +- Provide project and platform versions, depending on what seems relevant. + +## I want to contribute + +### Legal notice + +When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. + +### Code Style + +Please ensure that your code follows the existing code style conventions. We use the Dart language and follow the effective Dart guidelines. To maintain code consistency, consider using the Dart formatter to format your code automatically. + +Some hooks are also provided. To use them automatically, run +```sh +chmod +x .githooks/* +git config core.hooksPath .githooks +``` + +### Submitting changes + +Here are the recommands steps to follow to submit your changes. + +1. Fork the splitr repository on GitHub. +2. Create a new branch from the `master` branch with a descriptive name for your changes. +3. Ensure to follow the current guidelines. +4. Open a pull request (PR) against the `master` branch. +5. Provide a detailed description of your changes in the PR, including any relevant information or context. + +Thank you for your contribution! We appreciate your effort in making Splitr better. \ No newline at end of file diff --git a/README.md b/README.md index f1c4454..306c75b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This is currently a beta version. To get the app, you have to build it yourself | | | | |:-------------------------:|:-------------------------:|:-------------------------:| -![Project](https://raw.githubusercontent.com/BhasherBEL/Splitr/master/metadata/en-US/images/phoneScreenshots/1.png) | ![Refund](https://raw.githubusercontent.com/BhasherBEL/Splitr/master/metadata/en-US/images/phoneScreenshots/2.png) | ![New project](https://raw.githubusercontent.com/BhasherBEL/Splitr/master/metadata/en-US/images/phoneScreenshots/3.png) | +![Project](metadata/en-US/images/phoneScreenshots/1.png) | ![Refund](metadata/en-US/images/phoneScreenshots/2.png) | ![New project](metadata/en-US/images/phoneScreenshots/3.png) | ## Features diff --git a/analysis_options.yaml b/analysis_options.yaml index 61b6c4d..6bfa8e5 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,29 +1,6 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + prefer_single_quotes: true + \ No newline at end of file diff --git a/lib/data/local/item.dart b/lib/data/local/item.dart index e334c51..4cf16b9 100644 --- a/lib/data/local/item.dart +++ b/lib/data/local/item.dart @@ -17,7 +17,7 @@ class LocalItem extends LocalGeneric { item.itemParts = (await AppData.db.query( tableItemParts, columns: ItemPartFields.values, - where: "${ItemPartFields.itemId} = ?", + where: '${ItemPartFields.itemId} = ?', whereArgs: [item.localId], )) .map((e) => ItemPart.fromJson(e, item)) diff --git a/lib/data/pocketbase/item.dart b/lib/data/pocketbase/item.dart index 5075389..f76b03c 100644 --- a/lib/data/pocketbase/item.dart +++ b/lib/data/pocketbase/item.dart @@ -10,13 +10,13 @@ import '../../models/project.dart'; import 'item_part.dart'; class PocketBaseItemsFields { - static const String id = "id"; - static const String title = "title"; - static const String emitterId = "emitter_id"; - static const String projectId = "project_id"; - static const String amount = "amount"; - static const String date = "date"; - static const String deleted = "deleted"; + static const String id = 'id'; + static const String title = 'title'; + static const String emitterId = 'emitter_id'; + static const String projectId = 'project_id'; + static const String amount = 'amount'; + static const String date = 'date'; + static const String deleted = 'deleted'; } class PocketBaseItem { @@ -70,7 +70,7 @@ class PocketBaseItem { } static Future sync(PocketBase pb, Project project) async { - RecordService collection = pb.collection("items"); + RecordService collection = pb.collection('items'); // Get new dist records List records = await collection.getFullList( diff --git a/lib/data/pocketbase/item_part.dart b/lib/data/pocketbase/item_part.dart index 58e4f64..424a137 100644 --- a/lib/data/pocketbase/item_part.dart +++ b/lib/data/pocketbase/item_part.dart @@ -9,12 +9,12 @@ import '../../models/item_part.dart'; import '../../models/participant.dart'; class PocketBaseItemPartsFields { - static const String id = "id"; - static const String itemId = "item_id"; - static const String participantId = "participant_id"; - static const String rate = "rate"; - static const String amount = "amount"; - static const String deleted = "deleted"; + static const String id = 'id'; + static const String itemId = 'item_id'; + static const String participantId = 'participant_id'; + static const String rate = 'rate'; + static const String amount = 'amount'; + static const String deleted = 'deleted'; } class PocketBaseItemPart { @@ -62,7 +62,7 @@ class PocketBaseItemPart { } static Future sync(PocketBase pb, Item item) async { - RecordService collection = pb.collection("itemParts"); + RecordService collection = pb.collection('itemParts'); // Get new dist records List records = await collection.getFullList( diff --git a/lib/data/pocketbase/participant.dart b/lib/data/pocketbase/participant.dart index a249a47..409f739 100644 --- a/lib/data/pocketbase/participant.dart +++ b/lib/data/pocketbase/participant.dart @@ -8,10 +8,10 @@ import '../../models/participant.dart'; import '../../models/project.dart'; class PocketBaseParticipantFields { - static const String id = "id"; - static const String pseudo = "pseudo"; - static const String projectId = "project_id"; - static const String deleted = "deleted"; + static const String id = 'id'; + static const String pseudo = 'pseudo'; + static const String projectId = 'project_id'; + static const String deleted = 'deleted'; } class PocketBaseParticipant { @@ -51,7 +51,7 @@ class PocketBaseParticipant { } static Future sync(PocketBase pb, Project project) async { - RecordService collection = pb.collection("participants"); + RecordService collection = pb.collection('participants'); // Get new dist records List records = await collection.getFullList( diff --git a/lib/data/pocketbase/project.dart b/lib/data/pocketbase/project.dart index f6bd1ed..ecfbeb7 100644 --- a/lib/data/pocketbase/project.dart +++ b/lib/data/pocketbase/project.dart @@ -5,14 +5,14 @@ import 'package:splitr/utils/ext/record_service.dart'; import '../../models/project.dart'; class PocketBaseProjectFields { - static const String name = "name"; - static const String code = "code"; - static const String deleted = "deleted"; + static const String name = 'name'; + static const String code = 'code'; + static const String deleted = 'deleted'; } class PocketBaseProject { static Future sync(PocketBase pb, Project project) async { - RecordService collection = pb.collection("projects"); + RecordService collection = pb.collection('projects'); if (project.remoteId != null) { RecordModel record = await collection.getOne(project.remoteId!); @@ -49,9 +49,9 @@ class PocketBaseProject { } static Future join(PocketBase pb, Project project) async { - RecordService collection = pb.collection("projects"); + RecordService collection = pb.collection('projects'); RecordModel record = - await collection.getFirstListItem("code = \"${project.name}\""); + await collection.getFirstListItem('code = "${project.name}"'); project.code = project.name; project.name = record.getStringValue(PocketBaseProjectFields.name); project.remoteId = record.id; diff --git a/lib/data/pocketbase/provider.dart b/lib/data/pocketbase/provider.dart index 733e46e..8a530b5 100644 --- a/lib/data/pocketbase/provider.dart +++ b/lib/data/pocketbase/provider.dart @@ -3,7 +3,6 @@ import 'package:pocketbase/pocketbase.dart'; import '../../models/instance.dart'; import '../../models/item.dart'; -import '../../models/item_part.dart'; import '../../models/project.dart'; import '../provider.dart'; import 'item.dart'; diff --git a/lib/main.dart b/lib/main.dart index 327b0e0..e292c91 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'screens/project/project_page.dart'; import 'screens/projects_list/projects_list_page.dart'; import 'models/app_data.dart'; -import 'screens/project/new_project.dart'; +import 'screens/new_project/new_project.dart'; import 'utils/helper/theme.dart'; void main() async { @@ -27,7 +27,7 @@ class SplashScreen extends StatelessWidget { darkTheme: defaultDarkTheme, home: const Scaffold( body: Center( - child: Text("Starting up Splitr ..."), + child: Text('Starting up Splitr ...'), ), ), ); diff --git a/lib/models/app_data.dart b/lib/models/app_data.dart index 78737ea..53af673 100644 --- a/lib/models/app_data.dart +++ b/lib/models/app_data.dart @@ -6,7 +6,7 @@ import 'package:splitr/data/local/project.dart'; import 'package:splitr/utils/ext/set.dart'; import 'package:sqflite/sqflite.dart'; -import '../screens/project/new_project.dart'; +import '../screens/new_project/new_project.dart'; import '../services/database.dart'; import '../utils/helper/theme.dart'; import 'instance.dart'; @@ -27,7 +27,7 @@ class AppData { static set firstRun(bool v) { _firstRun = v; - sharedPreferences.setBool("firstRun", v); + sharedPreferences.setBool('firstRun', v); } static Project? get current { @@ -36,10 +36,10 @@ class AppData { static set current(Project? project) { if (project == null) { - sharedPreferences.remove("lastProject"); + sharedPreferences.remove('lastProject'); } else { sharedPreferences.setString( - "lastProject", + 'lastProject', project.name, ); } @@ -55,20 +55,20 @@ class AppData { AppData.projects = await Project.getAllProjects(); - if (!sharedPreferences.containsKey("firstRun")) { + if (!sharedPreferences.containsKey('firstRun')) { firstRun = AppData.projects.enabled().isEmpty; } else { - firstRun = sharedPreferences.getBool("firstRun")!; + firstRun = sharedPreferences.getBool('firstRun')!; } - if (sharedPreferences.containsKey("lastProject")) { + if (sharedPreferences.containsKey('lastProject')) { try { _current = - Project.fromName(sharedPreferences.getString("lastProject")!); + Project.fromName(sharedPreferences.getString('lastProject')!); await (_current!.conn as LocalProject).loadParticipants(); await (_current!.conn as LocalProject).loadEntries(); } catch (e) { - sharedPreferences.remove("lastProject"); + sharedPreferences.remove('lastProject'); } } diff --git a/lib/models/bill_data.dart b/lib/models/bill_data.dart index 34b5a93..a82c189 100644 --- a/lib/models/bill_data.dart +++ b/lib/models/bill_data.dart @@ -11,7 +11,7 @@ class BillData { this.item, required this.project, }) { - title = item?.title ?? ""; + title = item?.title ?? ''; date = item?.date ?? DateTime.now(); emitter = item?.emitter ?? project.currentParticipant ?? @@ -29,7 +29,7 @@ class BillData { Item? item; Project project; - String title = ""; + String title = ''; DateTime date = DateTime.now(); late Participant emitter; double amount = 0; @@ -87,8 +87,6 @@ shares: ${shares.entries.map((e) => "${e.key.pseudo}:${e.value}").join(",")}"""; } await item!.conn.save(); - List previous = item!.itemParts.toList(); - for (var a in shares.entries) { Participant p = a.key; BillPart s = a.value; diff --git a/lib/models/item.dart b/lib/models/item.dart index 5bc501c..b0d45a9 100644 --- a/lib/models/item.dart +++ b/lib/models/item.dart @@ -121,12 +121,12 @@ class Item extends Data { itemParts.enabled().map((e) => e.participant).toList(); if (participants.length < 4) { - return participants.map((e) => e.pseudo).join(", "); + return participants.map((e) => e.pseudo).join(', '); } if (participants.length == project.participants.length) return 'All'; List possibilites = [ - participants.map((e) => e.pseudo).join(", "), + participants.map((e) => e.pseudo).join(', '), 'All except ${project.participants.where((element) => !participants.contains(element)).map((e) => e.pseudo).join(', ')}', ]; diff --git a/lib/models/project.dart b/lib/models/project.dart index 1d32add..edbd507 100644 --- a/lib/models/project.dart +++ b/lib/models/project.dart @@ -170,7 +170,6 @@ class Project extends Data { return Tuple2(res, (DateTime.now().difference(st).inMilliseconds / 1000).toString()); } catch (e) { - print(e); return Tuple2(false, e.toString()); } } diff --git a/lib/screens/instances/instance_tile.dart b/lib/screens/instances/instance_tile.dart index b4cc80c..69b3189 100644 --- a/lib/screens/instances/instance_tile.dart +++ b/lib/screens/instances/instance_tile.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:splitr/data/local/instance.dart'; import '../../models/app_data.dart'; import '../../models/instance.dart'; @@ -47,9 +46,9 @@ class _InstanceTileState extends State { onPressed: () async { await confirmBox( context: context, - title: "Remove ${widget.instance.name}", + title: 'Remove ${widget.instance.name}', content: - "Are you sure you want to remove ${widget.instance.name}? You will not be able to sync or create projects with it.", + 'Are you sure you want to remove ${widget.instance.name}? You will not be able to sync or create projects with it.', onValidate: () async { await widget.instance.conn.delete(); AppData.instances.remove(widget.instance); diff --git a/lib/screens/instances/instances_list_page.dart b/lib/screens/instances/instances_list_page.dart index 1893e6b..0a77707 100644 --- a/lib/screens/instances/instances_list_page.dart +++ b/lib/screens/instances/instances_list_page.dart @@ -21,7 +21,7 @@ class _InstancesListPageState extends State { centerTitle: true, elevation: 4, title: const Text( - "Instances", + 'Instances', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, diff --git a/lib/screens/new_instance/new_instance_page.dart b/lib/screens/new_instance/new_instance_page.dart index 2bafa8c..157c81d 100644 --- a/lib/screens/new_instance/new_instance_page.dart +++ b/lib/screens/new_instance/new_instance_page.dart @@ -29,7 +29,7 @@ class NewInstancePage extends StatelessWidget { centerTitle: true, elevation: 4, title: const Text( - "New instance", + 'New instance', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, @@ -68,7 +68,7 @@ class NewInstancePage extends StatelessWidget { } try { if (!await Provider.checkCredentials(newInstance)) { - throw Exception("Failed to connect"); + throw Exception('Failed to connect'); } } on ClientException catch (e) { PocketBaseProvider.onClientException(e, context); @@ -104,8 +104,8 @@ class NewInstancePage extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(15.0), child: Text(instance == null - ? "Create instance" - : "Update instance"), + ? 'Create instance' + : 'Update instance'), ), ), ), diff --git a/lib/screens/new_instance/new_instance_pocketbase.dart b/lib/screens/new_instance/new_instance_pocketbase.dart index 98ae762..21594f7 100644 --- a/lib/screens/new_instance/new_instance_pocketbase.dart +++ b/lib/screens/new_instance/new_instance_pocketbase.dart @@ -26,7 +26,7 @@ class _NewInstancePocketbaseState extends State { onChanged: (value) => widget.instanceData.data['instance'] = value, decoration: const InputDecoration( - labelText: "Instance URL", + labelText: 'Instance URL', border: OutlineInputBorder(), ), ), @@ -44,7 +44,7 @@ class _NewInstancePocketbaseState extends State { ? 'Username can\'t be empty' : null, decoration: const InputDecoration( - labelText: "Username", + labelText: 'Username', border: OutlineInputBorder(), ), ), @@ -62,7 +62,7 @@ class _NewInstancePocketbaseState extends State { : null, obscureText: true, decoration: const InputDecoration( - labelText: "Password", + labelText: 'Password', border: OutlineInputBorder(), ), ), diff --git a/lib/screens/new_instance/new_instance_selector.dart b/lib/screens/new_instance/new_instance_selector.dart index 20ff39f..a207f5b 100644 --- a/lib/screens/new_instance/new_instance_selector.dart +++ b/lib/screens/new_instance/new_instance_selector.dart @@ -27,12 +27,12 @@ class _NewInstanceSelectorState extends State { initialValue: widget.instanceData.name, onChanged: (value) => widget.instanceData.name = value, decoration: const InputDecoration( - labelText: "Friendly name", + labelText: 'Friendly name', border: OutlineInputBorder(), ), ), ), - const HeaderTile("Instance type"), + const HeaderTile('Instance type'), ListTile( leading: Radio( value: 'pocketbase', @@ -43,10 +43,10 @@ class _NewInstanceSelectorState extends State { }); }, ), - title: const Text("Pocketbase"), + title: const Text('Pocketbase'), ), - const HeaderTile("Instance parameters"), - if (widget.instanceData.type == "pocketbase") + const HeaderTile('Instance parameters'), + if (widget.instanceData.type == 'pocketbase') Padding( padding: const EdgeInsets.all(8.0), child: NewInstancePocketbase(widget.instanceData), diff --git a/lib/screens/project/new_project.dart b/lib/screens/new_project/new_project.dart similarity index 77% rename from lib/screens/project/new_project.dart rename to lib/screens/new_project/new_project.dart index 704da8e..d74b288 100644 --- a/lib/screens/project/new_project.dart +++ b/lib/screens/new_project/new_project.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:pocketbase/pocketbase.dart'; -import '../new_project/new_project_page.dart'; +import 'new_project_page.dart'; import '../../main.dart'; import '../../models/app_data.dart'; import '../../data/pocketbase/provider.dart'; @@ -22,35 +22,37 @@ class NewProjectScreen extends StatelessWidget { }); final bool first; - Project? project; + final Project? project; final String? code; final Instance? instance; - ProjectData setupData = ProjectData(); + final ProjectData setupData = ProjectData(); Future onValidate(context, formKey) async { - bool newProject = project == null; + bool isNew = project == null; if (formKey.currentState != null && formKey.currentState!.validate()) { - if (newProject) { - project = Project( + Project updatedProject; + if (isNew) { + updatedProject = Project( name: setupData.projectName!, code: setupData.join ? null : getRandom(5), instance: setupData.instance!, ); } else { - project!.name = setupData.projectName!; - project!.provider = Provider.initFromInstance( - project!, + updatedProject = project!; + updatedProject.name = setupData.projectName!; + updatedProject.provider = Provider.initFromInstance( + updatedProject, setupData.instance!, ); } try { - AppData.current = project; - await project!.conn.save(); + AppData.current = updatedProject; + await updatedProject.conn.save(); if (setupData.join) { - await project!.provider.joinWithTitle(); - await project!.sync(); + await updatedProject.provider.joinWithTitle(); + await updatedProject.sync(); } } on ClientException catch (e) { PocketBaseProvider.onClientException(e, context); @@ -70,12 +72,12 @@ class NewProjectScreen extends StatelessWidget { runApp(const MainScreen()); } else { if (context.mounted) { - if (newProject) { + if (isNew) { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => NewProjectScreen( - project: project, + project: updatedProject, ), ), ); @@ -94,9 +96,9 @@ class NewProjectScreen extends StatelessWidget { @override Widget build(BuildContext context) { setupData.instance = instance; - bool newProject = project == null; + bool isNew = project == null; - if (newProject && code != null) { + if (isNew && code != null) { setupData.projectName = code; setupData.join = true; } @@ -104,19 +106,19 @@ class NewProjectScreen extends StatelessWidget { return NewScreen( title: first ? 'Create your first project!' - : newProject + : isNew ? 'New project' : 'Update project', buttonTitle: first ? 'Let\'s started' - : newProject + : isNew ? 'Create' : 'Update', + onValidate: onValidate, child: NewProjectPage( projectData: setupData, project: project, ), - onValidate: onValidate, ); } } diff --git a/lib/screens/new_project/new_project_page.dart b/lib/screens/new_project/new_project_page.dart index e4a3af9..6acf624 100644 --- a/lib/screens/new_project/new_project_page.dart +++ b/lib/screens/new_project/new_project_page.dart @@ -65,7 +65,7 @@ class _NewProjectPageState extends State { }); }, decoration: InputDecoration( - labelText: "Project instance", + labelText: 'Project instance', border: const OutlineInputBorder(), suffixIcon: const Icon(Icons.arrow_drop_down), labelStyle: @@ -84,13 +84,13 @@ class _NewProjectPageState extends State { ], ), const SizedBox(height: 12), - const HeaderTile("Configuration"), + const HeaderTile('Configuration'), const SizedBox(height: 12), if (widget.project == null) TextSwitch( state: widget.projectData.join, - leftText: "Create", - rightText: "Join", + leftText: 'Create', + rightText: 'Join', onChanged: (v) => setState(() => widget.projectData.join = v), ), TextFormField( @@ -100,7 +100,7 @@ class _NewProjectPageState extends State { onChanged: (value) => widget.projectData.projectName = value, decoration: InputDecoration( labelText: - widget.projectData.join ? "Project code" : "Project title", + widget.projectData.join ? 'Project code' : 'Project title', border: const OutlineInputBorder(), ), ), @@ -112,7 +112,7 @@ class _NewProjectPageState extends State { SelectFormField( type: SelectFormFieldType.dropdown, initialValue: - widget.project!.currentParticipant?.pseudo ?? "anonymous", + widget.project!.currentParticipant?.pseudo ?? 'anonymous', items: [ ...widget.project!.participants.map>((e) => { 'value': e.pseudo, @@ -133,7 +133,7 @@ class _NewProjectPageState extends State { }); }, decoration: const InputDecoration( - labelText: "Who are you ?", + labelText: 'Who are you ?', border: OutlineInputBorder(), suffixIcon: Icon(Icons.arrow_drop_down), ), @@ -174,7 +174,7 @@ class _ParticipantListWidgetState extends State { Widget build(BuildContext context) { return Column( children: [ - const HeaderTile("Participants"), + const HeaderTile('Participants'), ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), diff --git a/lib/screens/new_project/participant_tile.dart b/lib/screens/new_project/participant_tile.dart index d180b4b..f99e7b7 100644 --- a/lib/screens/new_project/participant_tile.dart +++ b/lib/screens/new_project/participant_tile.dart @@ -49,7 +49,7 @@ class _ParticipantTileState extends State { maxLength: 30, maxLengthEnforcement: MaxLengthEnforcement.enforced, decoration: InputDecoration( - counterText: "", + counterText: '', border: edit ? null : InputBorder.none, ), controller: controller, @@ -85,9 +85,9 @@ class _ParticipantTileState extends State { if (hasParticipant) { await confirmBox( context: context, - title: "Remove ${widget.participant!.pseudo}", + title: 'Remove ${widget.participant!.pseudo}', content: - "Are you sure you want to remove ${widget.participant!.pseudo}? You will not be able to undo it.", + 'Are you sure you want to remove ${widget.participant!.pseudo}? You will not be able to undo it.', onValidate: () async { await widget.project .deleteParticipant(widget.participant!); diff --git a/lib/screens/project/balances/balancing_page_part.dart b/lib/screens/project/balances/balancing_page_part.dart index 53a9152..96cd02a 100644 --- a/lib/screens/project/balances/balancing_page_part.dart +++ b/lib/screens/project/balances/balancing_page_part.dart @@ -107,7 +107,7 @@ class SharePainter extends CustomPainter { var pseudoPainter = TextPainter( text: TextSpan( - text: participant.pseudo + (isMe ? " (Me)" : ""), + text: participant.pseudo + (isMe ? ' (Me)' : ''), style: TextStyle( fontWeight: isMe ? FontWeight.bold : FontWeight.normal, ), @@ -117,7 +117,7 @@ class SharePainter extends CustomPainter { var valuePainter = TextPainter( text: TextSpan( - text: "${share.toStringAsFixed(2)}€", + text: '${share.toStringAsFixed(2)}€', style: TextStyle( fontWeight: isMe ? FontWeight.bold : FontWeight.normal, ), diff --git a/lib/screens/project/balances/refund_page_part.dart b/lib/screens/project/balances/refund_page_part.dart index 1b70397..123eaf9 100644 --- a/lib/screens/project/balances/refund_page_part.dart +++ b/lib/screens/project/balances/refund_page_part.dart @@ -16,7 +16,7 @@ List getRefundPageTiles({ List tiles = []; - tiles.add(const HeaderTile("How to refund ?")); + tiles.add(const HeaderTile('How to refund ?')); if (hasMe) { tiles.add( @@ -24,7 +24,7 @@ List getRefundPageTiles({ title: Padding( padding: EdgeInsets.only(left: 8.0), child: Text( - "Best for me", + 'Best for me', ), ), ), @@ -44,7 +44,7 @@ List getRefundPageTiles({ title: Padding( padding: EdgeInsets.only(left: 8.0), child: Text( - "Best for everybody", + 'Best for everybody', ), ), ), @@ -76,7 +76,7 @@ List getRefundsOf({ if (share >= 0) { tiles.add( const ListTile( - title: Text("Nothing to refund!"), + title: Text('Nothing to refund!'), dense: true, ), ); @@ -95,7 +95,7 @@ List getRefundsOf({ tiles.add( ListTile( - title: Text("${participant.pseudo} -> ${p.pseudo}"), + title: Text('${participant.pseudo} -> ${p.pseudo}'), trailing: Text('${part.toStringAsFixed(2)} €'), dense: true, ), @@ -141,7 +141,7 @@ List getRefundsOfEverybody({ tiles.add( ListTile( - title: Text("${participant.pseudo} -> ${p.pseudo}"), + title: Text('${participant.pseudo} -> ${p.pseudo}'), trailing: Text('${part.toStringAsFixed(2)} €'), dense: true, ), @@ -154,7 +154,7 @@ List getRefundsOfEverybody({ if (tiles.isEmpty) { tiles.add( const ListTile( - title: Text("Nothing to refund!"), + title: Text('Nothing to refund!'), dense: true, ), ); diff --git a/lib/screens/project/expenses/item_list.dart b/lib/screens/project/expenses/item_list.dart index a440015..ad3a1f2 100644 --- a/lib/screens/project/expenses/item_list.dart +++ b/lib/screens/project/expenses/item_list.dart @@ -30,8 +30,8 @@ class _ItemListState extends State { SnackBar( content: Text( res.item1 - ? "Project synced in ${res.item2} seconds" - : "Error: ${res.item2}", + ? 'Project synced in ${res.item2} seconds' + : 'Error: ${res.item2}', ), ), ); @@ -141,7 +141,7 @@ class _ItemListState extends State { children: [ Expanded( child: Text( - "${item.emitter.pseudo} ➝ ${item.toParticipantsString()}", + '${item.emitter.pseudo} ➝ ${item.toParticipantsString()}', style: const TextStyle( fontStyle: FontStyle.italic), ), @@ -169,8 +169,8 @@ class _ItemListState extends State { ) : Center( child: Text(widget.project.participants.isEmpty - ? "Add your first participant!" - : "Add your first item!"), + ? 'Add your first participant!' + : 'Add your first item!'), ), ), ], @@ -210,7 +210,7 @@ class _DynamicSyncState extends State { @override Widget build(BuildContext context) { - return Text("Last sync ${widget.time.timeElapsed()}"); + return Text('Last sync ${widget.time.timeElapsed()}'); } } @@ -251,7 +251,7 @@ class _SyncTileState extends State { Widget build(BuildContext context) { return ListTile( subtitle: Text( - "${widget.project.notSyncCount} changes to push", + '${widget.project.notSyncCount} changes to push', style: const TextStyle(fontStyle: FontStyle.italic), ), trailing: isSyncing diff --git a/lib/screens/project/expenses/new_entry.dart b/lib/screens/project/expenses/new_entry.dart index d63c75b..cca9ae6 100644 --- a/lib/screens/project/expenses/new_entry.dart +++ b/lib/screens/project/expenses/new_entry.dart @@ -77,12 +77,12 @@ class _NewEntrySubPageState extends State { for (Participant participant in widget.project.participants) { if (widget.item == null) { widget.bill.shares[participant] = BillPart(share: 1); - sharesController[participant] = TextEditingController(text: "1"); + sharesController[participant] = TextEditingController(text: '1'); fixedsController[participant] = TextEditingController(); } else if (widget.bill.shares[participant] == null) { widget.bill.shares[participant] = BillPart(); - sharesController[participant] = TextEditingController(text: "0"); - fixedsController[participant] = TextEditingController(text: "0.00"); + sharesController[participant] = TextEditingController(text: '0'); + fixedsController[participant] = TextEditingController(text: '0.00'); } else { sharesController[participant] = TextEditingController(); fixedsController[participant] = TextEditingController(); @@ -107,7 +107,7 @@ class _NewEntrySubPageState extends State { autocorrect: true, decoration: const InputDecoration( contentPadding: EdgeInsets.symmetric(horizontal: 10), - labelText: "What", + labelText: 'What', border: OutlineInputBorder(), ), onChanged: (value) => widget.bill.title = value, @@ -138,7 +138,7 @@ class _NewEntrySubPageState extends State { decoration: const InputDecoration( suffixText: ' €', contentPadding: EdgeInsets.symmetric(horizontal: 10), - labelText: "How much", + labelText: 'How much', border: OutlineInputBorder(), ), controller: amountController, @@ -166,7 +166,7 @@ class _NewEntrySubPageState extends State { type: SelectFormFieldType.dropdown, decoration: const InputDecoration( contentPadding: EdgeInsets.symmetric(horizontal: 10), - labelText: "Who paid", + labelText: 'Who paid', border: OutlineInputBorder(), ), items: widget.project.participants @@ -195,7 +195,7 @@ class _NewEntrySubPageState extends State { controller: dateController, decoration: const InputDecoration( contentPadding: EdgeInsets.symmetric(horizontal: 10), - labelText: "When", + labelText: 'When', border: OutlineInputBorder(), hintText: 'Pick your Date', ), @@ -250,9 +250,9 @@ class _NewEntrySubPageState extends State { ), ), ), - const TableHeaderCell(text: "For whom ?"), - const TableHeaderCell(text: "Rate"), - const TableHeaderCell(text: "Total"), + const TableHeaderCell(text: 'For whom ?'), + const TableHeaderCell(text: 'Rate'), + const TableHeaderCell(text: 'Total'), ], ), ...getRows(widget.bill, sharesController, fixedsController), @@ -279,8 +279,8 @@ class _NewEntrySubPageState extends State { widget.bill.shares.forEach((participant, amount) { final newShareValue = amount.share == null ? amount.fixed == null - ? "0" - : "" + ? '0' + : '' : amount.share!.toInt().toString(); if (newShareValue != sharesController[participant]!.text) { @@ -297,7 +297,7 @@ class _NewEntrySubPageState extends State { if (price.isNaN) price = 0; try { - if (fixedsController[participant]!.text == "" || + if (fixedsController[participant]!.text == '' || double.parse(fixedsController[participant]!.text) != price) { fixedsController[participant]!.text = price.toStringAsFixed(2); } diff --git a/lib/screens/project/project_page.dart b/lib/screens/project/project_page.dart index d0ec82c..f46ff8d 100644 --- a/lib/screens/project/project_page.dart +++ b/lib/screens/project/project_page.dart @@ -4,7 +4,7 @@ import 'package:share_plus/share_plus.dart'; import 'package:splitr/utils/ext/list.dart'; import '../../models/project.dart'; -import 'new_project.dart'; +import '../new_project/new_project.dart'; import '../projects_list/projects_list_page.dart'; import 'balances/balancing_page_part.dart'; import 'expenses/new_entry.dart'; @@ -77,11 +77,11 @@ class _ProjectPageState extends State { // ), BottomNavigationBarItem( icon: Icon(Icons.list), - label: "Expenses", + label: 'Expenses', ), BottomNavigationBarItem( icon: Icon(Icons.compare_arrows), - label: "Balances", + label: 'Balances', ), ], )); @@ -95,7 +95,7 @@ class _ProjectPageState extends State { showDialog( context: context, builder: (context) => AlertDialog( - title: const Text("Share project"), + title: const Text('Share project'), content: const Text( "You're about to share this project. Please note that in order to join, you must already be connected to the same instance."), actions: [ @@ -103,17 +103,17 @@ class _ProjectPageState extends State { onPressed: () => Share.share( 'Join my Splitr project!\n\nInstance: ${Uri.encodeComponent(widget.project.provider.instance.name)}\nCode: ${widget.project.code}', ), - child: const Text("Share code"), + child: const Text('Share code'), ), TextButton( onPressed: () => Share.share( 'Join my Splitr project!\nhttps://splitr.bhasher.com/join?instance=${Uri.encodeComponent(widget.project.provider.instance.name)}&code=${widget.project.code}', ), - child: const Text("Share link"), + child: const Text('Share link'), ), TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text("Cancel"), + child: const Text('Cancel'), ), ], ), @@ -146,10 +146,10 @@ class _ProjectPageState extends State { } }, itemBuilder: (context) => [ - buildMenuItem(value: 0, text: "Share", icon: Icons.share), - buildMenuItem(value: 1, text: "Edit project", icon: Icons.edit), + buildMenuItem(value: 0, text: 'Share', icon: Icons.share), + buildMenuItem(value: 1, text: 'Edit project', icon: Icons.edit), // buildMenuItem(value: 2, text: "Settings", icon: Icons.settings), - buildMenuItem(value: 3, text: "Close", icon: Icons.close), + buildMenuItem(value: 3, text: 'Close', icon: Icons.close), ], ); } diff --git a/lib/screens/projects_list/projects_list.dart b/lib/screens/projects_list/projects_list.dart index a24cf9b..db016ec 100644 --- a/lib/screens/projects_list/projects_list.dart +++ b/lib/screens/projects_list/projects_list.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:splitr/data/local/project.dart'; -import 'package:splitr/utils/ext/list.dart'; import 'package:splitr/utils/ext/set.dart'; import '../../models/app_data.dart'; import '../../models/project.dart'; -import '../project/new_project.dart'; +import '../new_project/new_project.dart'; import '../../utils/helper/navigator.dart'; import '../project/project_page.dart'; @@ -78,7 +77,7 @@ class _ProjectsListState extends State { }, title: Text(project.name), subtitle: Text( - "${project.provider.instance.name} (${project.provider.instance.type})", + '${project.provider.instance.name} (${project.provider.instance.type})', style: const TextStyle( fontStyle: FontStyle.italic, ), @@ -89,7 +88,7 @@ class _ProjectsListState extends State { itemCount: AppData.projects.enabled().length, ) : const Center( - child: Text("Create your first project!"), + child: Text('Create your first project!'), ); } } diff --git a/lib/screens/projects_list/projects_list_page.dart b/lib/screens/projects_list/projects_list_page.dart index 05a0406..61f0d50 100644 --- a/lib/screens/projects_list/projects_list_page.dart +++ b/lib/screens/projects_list/projects_list_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../project/new_project.dart'; +import '../new_project/new_project.dart'; import 'projects_list.dart'; class ProjectsListPage extends StatefulWidget { @@ -18,7 +18,7 @@ class _ProjectsListPageState extends State { centerTitle: true, elevation: 4, title: const Text( - "Splitr", + 'Splitr', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, diff --git a/lib/services/database.dart b/lib/services/database.dart index 5bbbd25..4420154 100644 --- a/lib/services/database.dart +++ b/lib/services/database.dart @@ -106,8 +106,8 @@ CREATE TABLE $tableInstances ( '''); await db.insert(tableInstances, { - InstanceFields.type: "local", - InstanceFields.name: "local", + InstanceFields.type: 'local', + InstanceFields.name: 'local', InstanceFields.data: json.encode({}), }); } @@ -130,8 +130,8 @@ CREATE TABLE $tableInstances ( '''); await db.insert(tableInstances, { - InstanceFields.type: "local", - InstanceFields.name: "local", + InstanceFields.type: 'local', + InstanceFields.name: 'local', InstanceFields.data: json.encode({}), }); } diff --git a/lib/utils/ext/string.dart b/lib/utils/ext/string.dart index 3612723..d846636 100644 --- a/lib/utils/ext/string.dart +++ b/lib/utils/ext/string.dart @@ -1,9 +1,9 @@ extension StringExtension on String { String capitalize() { - return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; + return '${this[0].toUpperCase()}${substring(1).toLowerCase()}'; } String firstCapitalize() { - return "${this[0].toUpperCase()}${substring(1)}"; + return '${this[0].toUpperCase()}${substring(1)}'; } } diff --git a/lib/utils/ext/text_input_formatter.dart b/lib/utils/ext/text_input_formatter.dart index 1669f6b..e5d1e68 100644 --- a/lib/utils/ext/text_input_formatter.dart +++ b/lib/utils/ext/text_input_formatter.dart @@ -29,7 +29,7 @@ class DecimalTextInputFormatter extends TextInputFormatter { // ); // } - if (truncated == ".") { + if (truncated == '.') { truncated = '0.'; newSelection = newValue.selection.copyWith( baseOffset: min(truncated.length, truncated.length + 1), diff --git a/lib/utils/helper/confirm_box.dart b/lib/utils/helper/confirm_box.dart index 8bc0b9d..72bf4ac 100644 --- a/lib/utils/helper/confirm_box.dart +++ b/lib/utils/helper/confirm_box.dart @@ -14,11 +14,11 @@ Future confirmBox({ actions: [ TextButton( onPressed: onValidate, - child: const Text("Yes"), + child: const Text('Yes'), ), TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text("No"), + child: const Text('No'), ), ], ), diff --git a/lib/widgets/new_screen.dart b/lib/widgets/new_screen.dart index 17c7f3f..30f53dd 100644 --- a/lib/widgets/new_screen.dart +++ b/lib/widgets/new_screen.dart @@ -9,12 +9,12 @@ class NewScreen extends StatelessWidget { this.buttonTitle, }); - String? title; - Widget child; - Future Function(BuildContext context, GlobalKey formKey)? + final String? title; + final Widget child; + final Future Function(BuildContext context, GlobalKey formKey)? onValidate; final GlobalKey formKey = GlobalKey(); - String? buttonTitle; + final String? buttonTitle; @override Widget build(BuildContext context) { @@ -53,7 +53,7 @@ class NewScreen extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(15.0), child: Text( - buttonTitle == null ? "Finish" : buttonTitle!), + buttonTitle == null ? 'Finish' : buttonTitle!), ), ), ), diff --git a/metadata/en-US/images/phoneScreenshots/1.png b/metadata/en-US/images/phoneScreenshots/1.png old mode 100644 new mode 100755 index 37be29c..762dc46 Binary files a/metadata/en-US/images/phoneScreenshots/1.png and b/metadata/en-US/images/phoneScreenshots/1.png differ diff --git a/metadata/en-US/images/phoneScreenshots/2.png b/metadata/en-US/images/phoneScreenshots/2.png old mode 100644 new mode 100755 index df50e1e..24041f8 Binary files a/metadata/en-US/images/phoneScreenshots/2.png and b/metadata/en-US/images/phoneScreenshots/2.png differ diff --git a/metadata/en-US/images/phoneScreenshots/3.png b/metadata/en-US/images/phoneScreenshots/3.png old mode 100644 new mode 100755 index a438490..34e15e3 Binary files a/metadata/en-US/images/phoneScreenshots/3.png and b/metadata/en-US/images/phoneScreenshots/3.png differ diff --git a/pubspec.yaml b/pubspec.yaml index 0c6d5cf..9b4d954 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: splitr is a free and open-source app that lets you create multiple publish_to: 'none' -version: 0.4.0 +version: 0.4.1 environment: sdk: '>=2.18.6 <3.0.0'