Flutter : Wings File Structure
Wings is an MVC file structure build with getx for flutter projects. to reduce the time of development and increase the programmer productivity.
-
core
-
features
here you can add your app features as the recommended structure (MVC)
- featureName
- controller
- model
- view
- featureName
clone the project to your device
git clone https://github.com/Invention-Technology/Flutter-Wings.git
go to the project directory and run flutter pub get
to install all the dependencies
1- inside feature folder create new folder and name it as your feature should be called, we will call it here home
2- create three folders with the following names: controller, model and view.
3- open model folder and create a new file with the name: home.model.dart
and add the following code inside it:
import 'package:wings/core/immutable/base/models/model.wings.dart';
class HomeModel implements WingsModel {
dynamic id; // add all the properties that will be fetched from the API and will be used inside the view
dynamic title;
HomeModel({
this.id,
this.title,
});
@override
List<WingsModel> fromJsonList(List<dynamic> json) {
List<HomeModel> models = [];
for (var element in json) {
models.add(HomeModel.fromJson(element));
}
return models;
}
factory HomeModel.fromJson(Map<String, dynamic> json) {
// add all the properties that you added above to assign the data that came from json to this instance
return HomeModel(id: json['id'], title: json['title']);
}
@override
WingsModel fromJson(Map<String, dynamic> json) {
return HomeModel.fromJson(json);
}
@override
Map<String, dynamic> toJson() {
// Map the data of this instance to json object to be send to the API
return {
'id': id,
'title': title,
};
}
}
4- open controller folder and create a new file with the name: home.controller.dart
and add the following code inside it:
import 'package:wings/core/immutable/base/controllers/controller.wings.dart';
import 'package:wings/core/immutable/providers/remote/request.wings.dart';
import 'package:wings/core/mutable/remote/urls.wings.dart';
import 'package:wings/features/index/model/index.model.dart';
class HomeController extends WingsController {
List<HomeModel> get posts => data;
@override
void onInit() async {
model = HomeModel(); // the model that coresponsidng to this feature
request = WingsRequest(url: WingsURL.home, shouldCache: true); // request data using REST API with the option caching the response
super.onInit();
}
}
5- open view folder and create a new file with the name: home.view.dart
and add the following code inside it:
import 'package:flutter/material.dart';
import 'package:wings/core/immutable/base/views/view.wings.dart';
class HomeView extends WingsView<HomeController> {
HomeView({Key? key}) : super(key: key, controller: HomeController());
@override
Widget successState(BuildContext context) {
return Text(controller.posts[0].title); // Title of the first post that came from the API
}
}
For more information, please see the provided example inside feature folder.
You can customize your theme by going to the core/mutable/themes/theme.wings.dart
and customize it as you want
Wings support translation using GetX translation for two languages by default ar
, en
-
Add new file for each language with the name
your_file.en.dart
andyour_file.ar.dart
. -
your files should have the following structure:
// each key should start with the file name capitalized camel case then : then the key capitalized camel case final Map<String, String> errorEn = { 'Error:ServerException': "Oops! We couldn't connect to the server", 'Error:CacheException': 'No Internet Connection', };
-
Add the variable that you just added to the file
_en.trans.dart
:import 'common.en.dart'; import 'error.en.dart'; import 'feedback.en.dart'; // add the variable name for each file ex: errorEn with spread operator final Map<String, String> en = { ...errorEn, ...commonEn, ...feedbackEn, };
-
Now you can add the translation keys to your project but don't delete the existent translation while you can change the translation itself
-
You can change the default language by changing the default language:
Wings.instance.defaultLanguage = WingsLanguage(locale: const Locale('ar'), textDirection: TextDirection.rtl);
Wings comes with variation of states for each use: Loading
, Error
, Success
, ErrorFlushBar
, SuccessFlushBar
with the ability to customize each one.
you can change the design for each one inside emutable/widgets/state
and emutable/helper/snack_bar
and you can add your custom widget or helper and override the default states inside mutable/widgets/states/app_state.wings.dart
import 'package:flutter/material.dart';
import '../../../immutable/providers/errors/error.model.wings.dart';
import '../../helpers/snack_bar/error_snack_bar.helper.wings.dart'
as error_snack_bar;
import '../../helpers/snack_bar/success_snack_bar.helper.wings.dart'
as success_snack_bar;
import 'error_state.widget.wings.dart';
import 'loading_state.widget.wings.dart';
class WingsAppState {
static Widget defaultWidgetState() {
return loading();
}
static Widget errorState({onRefresh, error}) {
return WingsErrorState(
onRefresh: onRefresh,
error: error,
);
}
static Widget loading() {
return const LoadingState();
}
static void successSnackBar({String message = '', String title = ''}) {
success_snack_bar.successSnackBar(message: message, title: title);
}
static void errorSnackBar({ErrorModel? error}) {
error_snack_bar.errorSnackBar(error: error);
}
}
Wings use REST API without patch request cause not all servers supports patch request
you can add your API URLs in mutable/remote/urls.wings.dart
class WingsURL {
static String get baseURL => 'https://jsonplaceholder.typicode.com/';
static String get posts => 'posts';
}
if you have a custom response format where the data come with extra info you can add your roles in mutable/remote/response_format.wings.dart
import 'dart:convert';
class WingsResponseFormat {
static String? key;
/// This function will check if the response is valid
/// according to the json structure that has been agreed on between
/// the backend and the flutter developer
///
/// The default wings validated response is according to JSON-API:
/// {"data": {...}}
///
/// This function require back-end api to always fill the data parameter
/// in the json response
///
/// if you don't like this json response,
/// please change this function to always return true without checking any code
/// static bool validatedResponse(String response) { return true; }
static bool validatedResponse(String response) {
return true; // TODO: uncomment this line if you don't have custom format
try {
key = 'data';
var data = jsonDecode(response);
if (data[key].toString().isEmpty) {
return false;
}
return true;
} catch (exception) {
return false;
}
}
}
to send a any request you have to use WingsRequest
class to specify the request information url
,body
,header
,queryString
request = WingsRequest(
url: url,
shouldCache: true,
body: {
"email":'test@email.com',
'password':"password"
},
);
in your controller
you can get/send data using getData()
,sendData()
. Those methods will read the request
variable and do the rest for you.
void login(){
request = WingsRequest(
url: WingsURL.login,
body: {
"email":'test@email.com',
'password':"password"
},
);
sendData();
}
you can access more methods using provider
. you have four methods to deal the the requests provider.get(request: request)
,provider.insert(request: request)
,provider.delete(request: request)
,provider.update(request: request)
.
the goal from statics is to avoid misspelling when using string in more than one place
Wings structure uses
WingsFeedback
,WingsErrorAssets
as statics
you can can customize your request messages (success and failures) and assets
- to customize request message go to
mutable/statics/feedback.static.dart
class WingsFeedback {
static String insertSuccess = 'Success:Insert'.tr;
}
- to customize request exceptions icons and images go
mutable/statics/error_asset.static.wings.dart
class WingsErrorAssets {
// Just in case that some icons or images deleted or not found, use those default values to handle the errors that may arise
static String defaultImage = 'assets/images/error.png';
static String defaultIcon = 'assets/icons/error.svg';
static String unexpectedErrorImage = '';
static String unexpectedErrorIcon = '';
//................
}
You can store data that you want to store and retrieve during the app life cycle
class WingsStore {
// add your data
}
All the helper functions that are commonly used throughout the project and it has a dependent task can be moved here.
All the widgets that are commonly used throughout the project and it has a dependent task can be moved here.
Wings uses the widgets inside
mutable/widgets/states
because they're commonly used throughout the structure. and can be customized by the developer.