diff --git a/.github/workflows/bump-version-based-on-labels.yaml b/.github/workflows/bump-version-based-on-labels.yaml index ae596fa..5995871 100644 --- a/.github/workflows/bump-version-based-on-labels.yaml +++ b/.github/workflows/bump-version-based-on-labels.yaml @@ -24,5 +24,5 @@ jobs: ruby-version: "2.6" bundler-cache: true working-directory: android - - run: bundle exec fastlane bump_version push:true branch:${{ github.head_ref }} ${{env.parts}} bump_build:false + - run: bundle exec fastlane bump_version branch:${{ github.head_ref }} ${{env.parts}} bump_build:false working-directory: android diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index 54fb831..5607bea 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -45,7 +45,7 @@ lane :bump_version do |options| parts: options[:bump], pubspec: "../pubspec.yaml" ) - if(options[:push] && options[:bump]) + if(options[:push] || options[:bump]) sh "git config --global user.email m97.chahboun@gmail.com" sh "git config --global user.name Mohammed chahboun" sh "git config --global push.followTags true" diff --git a/lib/main.dart b/lib/main.dart index 1f2d802..d57a88f 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter_ci_cd/views/counter_view.dart'; import 'package:flutter_ci_cd/views/mini_view.dart'; import 'package:flutter_ci_cd/views/photo_view.dart'; import 'package:flutter_ci_cd/views/post_view.dart'; +import 'package:flutter_ci_cd/views/user_view.dart'; import 'package:mvc_rocket/mvc_rocket.dart'; void main() { @@ -30,6 +31,9 @@ class App extends StatelessWidget { '/photo': (BuildContext context) => PhotoExample( title: "5000 Photos", ), + '/user': (BuildContext context) => UserExample( + title: "10 Users", + ), }, title: '🚀 MVCRocket 🚀 Package', theme: ThemeData( @@ -108,6 +112,7 @@ class MyApp extends StatelessWidget { const Example("Counter View", "counter"), const Example("100 Posts", "post"), const Example("5000 Photos", "photo"), + const Example("10 Users", "user"), ], ), ), diff --git a/lib/models/user_model.dart b/lib/models/user_model.dart new file mode 100755 index 0000000..cb20c03 --- /dev/null +++ b/lib/models/user_model.dart @@ -0,0 +1,96 @@ +import 'package:mvc_rocket/mvc_rocket.dart'; + +import 'user_submodel/address_submodel.dart'; +import 'user_submodel/company_submodel.dart'; + +const String userIdField = "id"; +const String userNameField = "name"; +const String userUsernameField = "username"; +const String userEmailField = "email"; +const String userAddressField = "address"; +const String userPhoneField = "phone"; +const String userWebsiteField = "website"; +const String userCompanyField = "company"; +const String userImageField = "image"; + +class User extends RocketModel { + int? id; + String? name; + String? username; + String? email; + Address? address; + String? phone; + String? website; + Company? company; + String? image; + + User( + {this.id, + this.name, + this.username, + this.email, + this.address, + this.phone, + this.website, + this.company, + this.image}) { + address ??= Address(); + company ??= Company(); + } + + @override + void fromJson(Map json, {bool isSub = false}) { + id = json[userIdField]; + name = json[userNameField]; + username = json[userUsernameField]; + email = json[userEmailField]; + address!.fromJson(json[userAddressField]); + phone = json[userPhoneField]; + website = json[userWebsiteField]; + image = json[userImageField]; + company!.fromJson(json[userCompanyField]); + super.fromJson(json, isSub: isSub); + } + + void updateFields({ + int? idField, + String? nameField, + String? usernameField, + String? emailField, + Address? addressField, + String? phoneField, + String? websiteField, + Company? companyField, + String? imageField, + }) { + id = idField ?? id; + name = nameField ?? name; + username = usernameField ?? username; + email = emailField ?? email; + address = addressField ?? address; + phone = phoneField ?? phone; + image = imageField ?? image; + website = websiteField ?? website; + company = companyField ?? company; + rebuildWidget(fromUpdate: true); + } + + @override + Map toJson() { + final Map data = {}; + data[userIdField] = id; + data[userNameField] = name; + data[userUsernameField] = username; + data[userEmailField] = email; + data[userAddressField] = address!.toJson(); + data[userPhoneField] = phone; + data[userWebsiteField] = website; + data[userCompanyField] = company!.toJson(); + data[userImageField] = image; + + return data; + } + + @override + get instance => User(); +} diff --git a/lib/models/user_submodel/address_submodel.dart b/lib/models/user_submodel/address_submodel.dart new file mode 100644 index 0000000..1cefb4f --- /dev/null +++ b/lib/models/user_submodel/address_submodel.dart @@ -0,0 +1,63 @@ +import 'package:flutter_ci_cd/models/user_submodel/geo_submodel.dart'; +import 'package:mvc_rocket/mvc_rocket.dart'; + +const String addressStreetField = "street"; +const String addressSuiteField = "suite"; +const String addressCityField = "city"; +const String addressZipcodeField = "zipcode"; +const String addressGeoField = "geo"; + +class Address extends RocketModel
{ + String? street; + String? suite; + String? city; + String? zipcode; + Geo? geo; + + Address({ + this.street, + this.suite, + this.city, + this.zipcode, + this.geo, + }) { + geo ??= Geo(); + } + + @override + void fromJson(Map json, {bool isSub = false}) { + street = json[addressStreetField]; + suite = json[addressSuiteField]; + city = json[addressCityField]; + zipcode = json[addressZipcodeField]; + geo!.fromJson(json[addressGeoField]); + super.fromJson(json, isSub: isSub); + } + + void updateFields({ + String? streetField, + String? suiteField, + String? cityField, + String? zipcodeField, + Geo? geoField, + }) { + street = streetField ?? street; + suite = suiteField ?? suite; + city = cityField ?? city; + zipcode = zipcodeField ?? zipcode; + geo = geoField ?? geo; + rebuildWidget(fromUpdate: true); + } + + @override + Map toJson() { + final Map data = {}; + data[addressStreetField] = street; + data[addressSuiteField] = suite; + data[addressCityField] = city; + data[addressZipcodeField] = zipcode; + data[addressGeoField] = geo!.toJson(); + + return data; + } +} diff --git a/lib/models/user_submodel/company_submodel.dart b/lib/models/user_submodel/company_submodel.dart new file mode 100644 index 0000000..07a0e0c --- /dev/null +++ b/lib/models/user_submodel/company_submodel.dart @@ -0,0 +1,46 @@ +import 'package:mvc_rocket/mvc_rocket.dart'; + +const String companyNameField = "name"; +const String companyCatchPhraseField = "catchPhrase"; +const String companyBsField = "bs"; + +class Company extends RocketModel { + String? name; + String? catchPhrase; + String? bs; + + Company({ + this.name, + this.catchPhrase, + this.bs, + }); + + @override + void fromJson(Map json, {bool isSub = false}) { + name = json[companyNameField]; + catchPhrase = json[companyCatchPhraseField]; + bs = json[companyBsField]; + super.fromJson(json, isSub: isSub); + } + + void updateFields({ + String? nameField, + String? catchPhraseField, + String? bsField, + }) { + name = nameField ?? name; + catchPhrase = catchPhraseField ?? catchPhrase; + bs = bsField ?? bs; + rebuildWidget(fromUpdate: true); + } + + @override + Map toJson() { + final Map data = {}; + data[companyNameField] = name; + data[companyCatchPhraseField] = catchPhrase; + data[companyBsField] = bs; + + return data; + } +} diff --git a/lib/models/user_submodel/geo_submodel.dart b/lib/models/user_submodel/geo_submodel.dart new file mode 100644 index 0000000..59dc4db --- /dev/null +++ b/lib/models/user_submodel/geo_submodel.dart @@ -0,0 +1,38 @@ +import 'package:mvc_rocket/mvc_rocket.dart'; + +const String geoLatField = "lat"; +const String geoLngField = "lng"; + +class Geo extends RocketModel { + String? lat; + String? lng; + + Geo({ + this.lat, + this.lng, + }); + @override + void fromJson(Map json, {bool isSub = false}) { + lat = json[geoLatField]; + lng = json[geoLngField]; + super.fromJson(json, isSub: isSub); + } + + void updateFields({ + String? latField, + String? lngField, + }) { + lat = latField ?? lat; + lng = lngField ?? lng; + rebuildWidget(fromUpdate: true); + } + + @override + Map toJson() { + final Map data = {}; + data[geoLatField] = lat; + data[geoLngField] = lng; + + return data; + } +} diff --git a/lib/requests/user_request.dart b/lib/requests/user_request.dart new file mode 100644 index 0000000..d9e1738 --- /dev/null +++ b/lib/requests/user_request.dart @@ -0,0 +1,9 @@ +import 'package:flutter_ci_cd/models/user_model.dart'; +import 'package:mvc_rocket/mvc_rocket.dart'; + +const String usersEndpoint = "users"; + +class GetUsers { + static Future getUsers(User userModel) => Rocket.get(rocketRequestKey) + .getObjData(usersEndpoint, userModel, multi: true); +} diff --git a/lib/views/user_view.dart b/lib/views/user_view.dart new file mode 100755 index 0000000..23d9008 --- /dev/null +++ b/lib/views/user_view.dart @@ -0,0 +1,219 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_ci_cd/models/user_model.dart'; +import 'package:flutter_ci_cd/models/user_submodel/address_submodel.dart'; +import 'package:flutter_ci_cd/models/user_submodel/company_submodel.dart'; +import 'package:flutter_ci_cd/models/user_submodel/geo_submodel.dart'; +import 'package:flutter_ci_cd/requests/user_request.dart'; +import 'package:mvc_rocket/mvc_rocket.dart'; + +class UserExample extends StatelessWidget { + final User users = Rocket.add(usersEndpoint, User()); + UserExample({Key? key, required this.title}) : super(key: key); + final String title; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(title), + ), + floatingActionButton: Container( + color: Theme.of(context).primaryColor, + child: TextButton( + child: Wrap( + children: const [Icon(Icons.get_app), Text("Get Data")], + ), + onPressed: () => GetUsers.getUsers(users), + )), + body: SizedBox( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: RocketView( + // call api by RocketRequest saved in McController and make model on ready + call: () => GetUsers.getUsers(users), + // call api every 2 sec + callType: CallType.callAsStream, + // update data from server every 2 sec + secondsOfStream: 2, + // your model + model: users, + // your widget for show data from model + builder: (context) { + return ListView.builder( + itemCount: users.multi!.length, + itemBuilder: (BuildContext context, int index) { + User user = users.multi![index]; + Company company = user.company!; + Address address = user.address!; + Geo geo = address.geo!; + return ExpansionTile( + leading: InkWell( + onLongPress: () => users.delItem(index), + onTap: () => Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) { + return OneUser(index); + })), + child: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + backgroundImage: user.image == null + ? null + : NetworkImage(user.image!), + child: user.image == null + ? const Icon(Icons.person) + : null, + ), + ), + title: Text("User :${user.name!}"), + trailing: IconButton( + icon: const Icon(Icons.update), + onPressed: () { + Company newCompany = Company( + bs: "change data...bs", + name: "Name changed", + catchPhrase: "change data...catch"); + // update user model + user.updateFields( + nameField: "Mohammed CHAHBOUN 💙", + companyField: newCompany, + ); + }), + children: [ + const SizedBox(height: 5.0), + Text(user.id.toString()), + Text(user.username!), + Text(user.email!), + Text(user.phone!), + Text(user.website!), + const SizedBox(height: 5), + ExpansionTile( + tilePadding: + const EdgeInsets.symmetric(horizontal: 40.0), + leading: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + child: const Icon(Icons.home), + ), + title: Text("Company :${company.name!}"), + children: [ + const SizedBox(height: 5.0), + Text(company.bs!), + Text(company.catchPhrase!), + ]), + const SizedBox(height: 5), + ExpansionTile( + tilePadding: + const EdgeInsets.symmetric(horizontal: 40.0), + leading: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + child: const Icon(Icons.place), + ), + title: Text("Address :${address.city!}"), + children: [ + const SizedBox(height: 5.0), + Text(address.street!), + Text(address.suite!), + Text(address.zipcode!), + Text(address.city!), + const SizedBox(height: 5.0), + ExpansionTile( + tilePadding: const EdgeInsets.symmetric( + horizontal: 80.0), + leading: CircleAvatar( + backgroundColor: + Theme.of(context).primaryColor, + child: const Icon(Icons.map), + ), + title: const Text("geo adrdress"), + children: [ + const SizedBox(height: 5.0), + Text(geo.lat!), + Text(geo.lng!), + ]), + ]), + ]); + }, + ); + }, + )), + ); + } +} + +// ignore: must_be_immutable +class OneUser extends StatelessWidget { + final int index; + late User user; + late Company company; + late Address address; + late Geo geo; + OneUser(this.index, {Key? key}) : super(key: key) { + user = Rocket.get(usersEndpoint).multi![index]; + company = user.company!; + address = user.address!; + geo = address.geo!; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: ExpansionTile( + leading: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + backgroundImage: + user.image == null ? null : NetworkImage(user.image!), + child: user.image == null ? const Icon(Icons.person) : null, + ), + title: Text("User :${user.name!}"), + children: [ + const SizedBox(height: 5.0), + Text(user.id.toString()), + Text(user.username!), + Text(user.email!), + Text(user.phone!), + Text(user.website!), + const SizedBox(height: 5), + ExpansionTile( + tilePadding: const EdgeInsets.symmetric(horizontal: 40.0), + leading: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + child: const Icon(Icons.home), + ), + title: Text("Company :${company.name!}"), + children: [ + const SizedBox(height: 5.0), + Text(company.bs!), + Text(company.catchPhrase!), + ]), + const SizedBox(height: 5), + ExpansionTile( + tilePadding: const EdgeInsets.symmetric(horizontal: 40.0), + leading: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + child: const Icon(Icons.place), + ), + title: Text("Address :${address.city!}"), + children: [ + const SizedBox(height: 5.0), + Text(address.street!), + Text(address.suite!), + Text(address.zipcode!), + Text(address.city!), + const SizedBox(height: 5.0), + ExpansionTile( + tilePadding: + const EdgeInsets.symmetric(horizontal: 80.0), + leading: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + child: const Icon(Icons.map), + ), + title: const Text("geo adrdress"), + children: [ + const SizedBox(height: 5.0), + Text(geo.lat!), + Text(geo.lng!), + ]), + ]), + ]), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 4b43352..3cea545 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.6.0+23 +version: 2.7.0+24 environment: sdk: ">=2.17.6 <3.0.0"