PLEX
`PLEX` is a UI framework for Flutter designed specifically for building enterprise applications. It provides a robust and customizable foundation for the entire application, including boilerplate code for main.dart file, routes, screens, and other essential components.
In addition to the basic application structure, PLEX
comes with pre-built components for common UI elements such as tableviews and forms. The tableviews can be easily customized to display data in various formats like lists and grids, while the forms can collect data from users using different
input
types like text fields, dropdowns, and checkboxes.
PLEX
also offers guidelines and best practices for building enterprise applications, including data management, architecture, testing, and more. These guidelines can help developers to build scalable, maintainable, and high-performance applications.
The PLEX
framework is an ideal choice for developers who want to build enterprise-level applications quickly and efficiently, without compromising on quality or customization. Its pre-built components and best practices can save developers a significant amount of time and effort, while also
ensuring
the resulting application meets the high standards of enterprise-level software.
Note:
PLEX
also provide a single click to move from Material 2
to Material 3
and Light Mode
to Dark Mode
.
Screenshots
Material 3


Material 2


Application Screen Shots












Features
- Create boilerplate code for an Application
- Built in login screen
- Built in User session manager
- Free useful widgets
- Free useful utilities
- Built in screens and pages
- Builtin form builder from model class
- Code Generation For Models i.e.
copy()
andasString()
method generation. - Dependency Injection based on TAGs.
- Support
MVVM
pattern by providingPlexViewModel
to help reduce boilerplate code and useful features - Easy to integrate
SignalR
in Flutter
Getting started
Install the plex
in your application.
plex: ^version
Usage
Microsoft SignalR
PlexSignalR.config = PlexSignalRConfig(
"https://serverurl:port",
"hubPath",
remoteMethods: [
//Define Local Methods To Be Invoked By Server
PlexSignalRMethod("OnEvent", (arguments) {
print(arguments);
}),
PlexSignalRMethod("NewTest", (arguments) {
print(arguments);
}),
],
onClose: (error) {},
onConnecting: (error) {},
);
await PlexSignalR.instance.start();
//Invoke Server Methods
PlexSignalR.instance.invoke("JoinGroup", ["FL1"]);
PlexSignalR.instance.invoke("JoinGroup", ["FL2"]);
//Invoke Server Methods
PlexSignalR.instance.invoke("SendEventToGroup", ["FL1", "From Group 1"]);
PlexSignalR.instance.invoke("SendEventToGroup", ["FL2", "From Group 2"]);
//Invoke Server Methods
PlexSignalR.instance.invoke("LeaveGroup", ["FL2"]);
//Invoke Server Methods
PlexSignalR.instance.invoke("SendEventToGroup", ["FL1", "From Group 1"]);
PlexSignalR.instance.invoke("SendEventToGroup", ["FL2", "From Group 2"]);
PlexSignalR.instance.invoke("SendEventToAllByMethod", ["NewTest", "For New Test"]);
Widgets
PlexWidget
- Updatable widget and controlled by a controller. Replaces the use of BLoC or provider pattern
PlexDataTable
- View Data in a tabular form. Sort By Any Column, Search By Any Column, Export as Excel builtin functions
PlexAdvanceDataTable
- Modern Data Table for more feature rich experience, Export as Excel & Pdf
PlexInputWidget
- Simple Widget to create a
TextInputField
,DropdownField
,DatePickerField
andMultiSelectionFiel
- There are lots of features available for each field
- Simple Widget to create a
PlexFormWidget
- Extend any model class with
PlexForm
andoverride
methodgetFields()
and configure UI fields. All the form layout will be created automatically.
- Extend any model class with
PlexLoader
- Show loading anywhere in application by displaying widget
PlexLoader
- Show loading anywhere in application by displaying widget
PlexShimmer
- Show shimmer widget when data is loading by displaying widget
PlexShimmer
- Show shimmer widget when data is loading by displaying widget
PlexInputWidget - V1
Usage
/// Input Types
/// PlexInputWidgetType.typeInput
/// PlexInputWidgetType.typeDropdown
/// PlexInputWidgetType.typeDate
/// PlexInputWidgetType.typeButton
PlexInputWidget(
title: "Username / Email",
type: PlexInputWidgetType.typeInput,
inputHint: "Enter Your Email or Username",
inputController: usernameController,
inputOnChange: (value) {},
inputOnSubmit: (value) {},
inputAction: TextInputAction.go,
inputKeyboardType: TextInputType.name,
isPassword: false,
dropdownItemOnSelect: (item) {},
dropdownItemAsString: (item) => item.toString(),
dropdownItems: const ["Data"],
dropdownAsyncItems: Future(() => ["Data"]),
dropdownSelectionController: PlexWidgetController(),
dropDownLeadingIcon: (item) => const Icon(Icons.add),
dropdownItemWidget: (item) => const Text("Data"),
dropdownOnSearch: (query, item) { return true; },
dropdownCustomOnTap: () {},
buttonClick: ,
buttonIcon: ,
buttonColor: ,
useMargin: ,
margin: ,
fieldColor: ,
editable: ,
helperText: ,
)
PlexInputWidget - V2
Usage
PlexFormFieldInput(...)
//PlexFormFieldDateType.typeDate
//PlexFormFieldDateType.typeTime
//PlexFormFieldDateType.typeDateTime
PlexFormFieldDate(type: PlexFormFieldDateType.typeDateTime, ...)
PlexFormFieldDropdown(...)
PlexFormFieldMultiSelect(...)
PlexFormFieldAutoComplete(...)
PlexFormFieldButton(...)
Persistent Storage
//Only initialize if you are not using PlexApp
//and using PlexDb separately
PlexDb.initialize();
PlexDb.instance.setString("Key", "Value");
PlexDb.instance.getString("Key");
PlexDb.instance.setBool("Key", true);
PlexDb.instance.getBool("Key");
Messaging
BuildContext context;
context.showSnackBar("Your Message...");
Utils
Dimension Utilities
Dim.mini //Dimension of 2
Dim.smallest //Dimension of 4
Dim.small //Dimension of 8
Dim.medium //Dimension of 16
Dim.large //Dimension of 32
Spacing Utilities
spaceMini() //Widget with height width 2
spaceSmallest() //Widget with height width 4
spaceSmall() //Widget with height width 8
spaceMedium() //Widget with height width 16
space(Any Double) //Widget with custom height anf width
Console Utilities
console("Your Message In Console", '(optional) enable print in release build')
Show Messages & Toast
//If Using PlexApp
showMessage("Your Message");
// If not using PlexAp
context.showMessage("Your Message");
//You can use following options as parameter
String message
String title,
Widget? titleWidget,
Widget? messageWidget,
MessageType type = MessageType.info,
MessageStyle style = MessageStyle.flatColored,
bool autoClose = true,
int autoCloseDurationSeconds = 5,
Alignment alignment = Alignment.bottomRight,
TextDirection textDirection = TextDirection.ltr,
bool showAnimation = false,
int animationDurationMillis = 300,
Widget? customIcon,
Widget Utilities
createWidget((){
//Any Calculation or Custom Logic Here...
return Container();
})
Async Utilities
//This will delay your code for 500 millis then do the work
//Usefully when you want to execute a task after navigation or when UI is still building
delay(() {
//Your Logic Here
return 'Any Object';
})
runAsync(() {
//Your Logic Here will be Asynchronously run
return "Any Object"
})
List Utilities
//List.sort() doesn't return anything so we need a extra line to sort the list og type T
//You can use this method to sort and use in one single line
List<T> result = List<T>.sortAndReturn();
//Will return a Map<Key, List<T>> by grouping the list on some condition
Map<String, List<User>> usersByCities = List<User>.groupBy((user) {
return user.city;
});
Loading Widget
PlexLoadingV1()
PlexLoadingV2()
String Utils
/// "2012-02-27"
/// "2012-02-27 13:27:00"
/// "2012-02-27 13:27:00.123456789z"
/// "2012-02-27 13:27:00,123456789z"
/// "20120227 13:27:00"
/// "20120227T132700"
/// "20120227"
/// "+20120227"
/// "2012-02-27T14Z"
/// "2012-02-27T14+00:00"
/// "-123450101 00:00:00 Z": in the year -12345.
/// "2002-02-27T14:00:00-0500"
/// "2002-02-27T19:00:00Z"
DateTime time = "2012-02-27 13:27:00".toDate();
DateTime Utilities
var dateInString = DateTime.now().toDDMMMHHmmss();
var dateInString = DateTime.now().toMMMDDYYYY();
//Convert DateTime to String in custom format
var dateInString = DateTime.now().toFormattedString("hh:mm:ss a");
// Will return the time difference in hours or minutes or in seconds
var timeDifference = "20120227 13:27:00".toDate().getDifferenceString();
Code Generation
Update your code like below:
1. Dependency
Add Dev Dependency in the pubspec.yaml
dev_dependencies:
# Other Dev Dependencies
build_runner: 2.4.8
# Other Dev Dependencies
2. Code Changes
import 'package:plex/plex_annotations/plex_annotations.dart';
/// 1. Add part file path in the file, name of the file
/// must be same as your model class file
part 'order.plex.dart';
/// 2. Annotate the model class with [plexAnnotationModel]
@plexAnnotationModel
class Order {
late String name;
late String id;
late List<String> names;
late double amount;
/// 3. Create an empty constructor with no argument in the model
Order();
}
3. Terminal Command
Run the following command in terminal of main directory of project
flutter pub run build_runner build --delete-conflicting-outputs
Dependency Injection
You can use dependency injection as below:
1. Inject Dependencies
///Test Model Class
class Model {
int modelId;
String modelTitle;
const Model(this.modelId, this.modelTitle);
}
/// Inject Singleton Object, You can use optional tag in it also
injectSingleton(Model(1, "Singleton Model"));
/// Inject Singleton Object Lazy, You can use optional tag in it also
injectSingletonLazy((parm) => const Model(1, parm["title"]), tag: "singleton_model");
/// Inject Factory Object that will be created everytime, You can use optional tag in it also
injectFactory((parm) => Model(2, parm["title"]), tag: "factory_model");
2. Get Dependencies
///Get the SINGLETON model like this
var singletonModel = fromPlex<Model>();
///Get the LAZY SINGLETON model like this
///With optional {parm} to be used by builder
var singletonModelLazy = fromPlex<Model>(tag: "singleton_model", parm: { "title": "Test Model"});
///Get the Factory model like this
///With optional {parm} to be used by builder
var factoryModel = fromPlex<Model>(parm: { "title": "Test Model"});
MVVM Usage
You can use MVVM ViewModel as below:
1. Create Your Screen i.e. HomeScreen
:
class HomeScreen extends PlexScreen {}
class _HomeScreenState extends PlexState<HomeScreen> {
showBottomSheet() {
///Example
///Show Bottom Sheet Logic Here
}
}
2. Create ViewModel
Your Screen HomeScreen
:
class HomeScreenViewModel extends PlexViewModel<HomeScreen, _HomeScreenState>{
/// Mode All Youe Data Access Logic Here
exampleFunction() {
showLoading();
hideLoading();
toast('Your Message');
toastDelayed('Your Message');
///state will be of type `_HomeScreenState`
///So you can call any function or access any field from '_HomeScreenState'
state?.showBottomSheet();
}
}
3. Initialize ViewModel
of any Screen and Use in _HomeScreenState
class _HomeScreenState extends PlexState<HomeScreen> {
/// If using Plex Dependency Injection
/// i.e. injectSingleton(HomeScreenViewModel());
var viewModel = fromPlex<HomeScreenViewModel>();
///Or Simply by creating object of view model
var viewModel = HomeScreenViewModel();
initState() {
super.initState();
viewModel.setState(this);
}
///Other Logic and Functions
///....
showBottomSheet() {
///Example
///Show Bottom Sheet Logic Here
}
}
Complete Example of Using PlexApp
- You can also see example project to see the whole project usage in action.
import 'package:flutter/material.dart';
import 'package:plex/plex_networking/plex_networking.dart';
import 'package:plex/plex_package.dart';
import 'package:plex/plex_route.dart';
import 'package:plex/plex_screens/plex_dashboard_screen.dart';
import 'package:plex/plex_screens/plex_login_screen.dart';
import 'package:plex/plex_user.dart';
import 'package:plex/plex_widgets/plex_data_table.dart';
import 'package:plex_app/screens/home_screen.dart';
import 'package:plex_app/screens/second_screen.dart';
class Routes {
static const dashboardScreen = "/dashboard";
static const secondScreen = "/second";
}
class AppUser extends PlexUser {
late String email;
late String userName;
List<String>? rules;
AppUser.init({required this.email, required this.userName, this.rules});
@override
String? getPictureUrl() {
return "https://images.pexels.com/photos/631317/pexels-photo-631317.jpeg";
}
@override
String getLoggedInEmail() => email;
@override
List<String>? getLoggedInRules() => rules;
@override
String getLoggedInUsername() => userName;
@override
String getLoggedInFullName() => userName;
@override
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['userName'] = userName;
map['email'] = email;
map['rules'] = rules;
return map;
}
AppUser.fromJson(Map<String, dynamic> map) {
userName = map["userName"];
email = map["email"];
rules = map["rules"];
}
}
void main() async {
getTableData() => [
[
PlexDataCell.text("1"),
PlexDataCell.text("First"),
PlexDataCell.text("Person"),
PlexDataCell.text("EMP953312RT"),
PlexDataCell.text("Software Engineer"),
PlexDataCell.text("Grade"),
PlexDataCell.custom(
"First Company Pvt. Ltd",
const DataCell(
Text("First Company Pvt. Ltd", style: TextStyle(color: Colors.brown)),
),
),
],
[
PlexDataCell.text("2"),
PlexDataCell.text("Second"),
PlexDataCell.text("Person"),
PlexDataCell.text("EMP95313RT"),
PlexDataCell.text("Software Engineer"),
PlexDataCell.text("Scale"),
PlexDataCell.custom(
"Second Company Pvt. Ltd",
const DataCell(
Text("Second Company Pvt. Ltd", style: TextStyle(color: Colors.green)),
),
)
],
];
///Setting Theme First Method : Customized Theme
PlexTheme.appTheme = ThemeData(
useMaterial3: true,
colorSchemeSeed: const Color(0xFF26A9E1),
);
runApp(PlexApp(
///Setting Theme Second Method : Theme By Color
// themeFromColor: const Color(0xFF26A9E1),
///Setting Theme Third Method : Theme By Image
// themeFromImage: const AssetImage("assets/img.png"),
appInfo: PlexAppInfo(
title: "Auto Backup",
appLogo: const Icon(Icons.account_balance),
appLogoDark: const Icon(Icons.account_balance, color: Colors.white),
initialRoute: Routes.dashboardScreen,
versionCode: 1,
versionName: "v1.0.0",
),
useAuthorization: true,
onInitializationComplete: () {
PlexNetworking.instance.allowBadCertificateForHTTPS();
},
loginConfig: PlexLoginConfig(
debugUsername: 'test',
debugPassword: 'password',
additionalWidgetsTop: (context) => const Text("Login Screen"),
additionalWidgetsBottom: (context) => const Text("Login Screen End"),
onLogin: (context, email, password) async {
return AppUser.init(userName: "Abdur Rahman", email: "ar@mail.com");
},
userFromJson: (userData) {
return AppUser.fromJson(userData);
},
),
dashboardConfig: PlexDashboardConfig(
disableExpandNavigationRail: false,
disableNavigationRail: false,
disableBottomNavigation: false,
showThemeSwitch: true,
showBrightnessSwitch: true,
showMaterialSwitch: true,
appbarActions: [
MenuItemButton(
leadingIcon: const Icon(Icons.abc_outlined),
child: const Text("ABC"),
onPressed: () {},
),
MenuItemButton(
leadingIcon: const Icon(Icons.account_tree_outlined),
child: const Text("Tree"),
onPressed: () {},
),
MenuItemButton(
leadingIcon: const Icon(Icons.account_balance_outlined),
child: const Text("Balance"),
onPressed: () {},
),
],
dashboardScreens: [
PlexRoute(
route: Routes.dashboardScreen,
category: "Tables",
title: "Data Table Widget Usage",
shortTitle: 'Data Table',
logo: const Icon(Icons.account_balance_outlined),
screen: (context) => PlexDataTable(
enableSearch: true,
enablePrint: true,
onRefresh: () {
getTableData();
},
headerTextStyle: const TextStyle(fontWeight: FontWeight.bold),
headerBackground: Colors.redAccent,
border: TableBorder.all(color: Colors.black12),
columns: [
PlexDataCell.text("Id"),
PlexDataCell.text("First Name"),
PlexDataCell.text("Last Name"),
PlexDataCell.text("Emp Code"),
PlexDataCell.text("Designation"),
PlexDataCell.text("Grade"),
PlexDataCell.text("Company"),
],
rows: List.empty(), //getTableData(),
),
),
PlexRoute(
route: "/paginated-table",
category: "Paginated Tables",
title: "Paginated Data Table",
shortTitle: 'Paginated Table',
logo: const Icon(Icons.account_balance_outlined),
screen: (context) => PlexDataTableWithPages(
columns: [
PlexDataCell.text("Id"),
PlexDataCell.text("First Name"),
PlexDataCell.text("Last Name"),
PlexDataCell.text("Emp Code"),
PlexDataCell.text("Designation"),
PlexDataCell.text("Grade"),
PlexDataCell.text("Company"),
],
rows: getTableData(),
),
),
PlexRoute(
route: "/update-widget",
title: "Updatable Widget Usage",
shortTitle: 'Updatable Widget',
logo: const Icon(Icons.browser_updated),
screen: (context) => const UpdatableScreen(),
),
PlexRoute(
route: "/form-usage",
title: "Form Widget Usage",
shortTitle: 'Form Widget',
logo: const Icon(Icons.format_align_center),
screen: (context) => const FormUsageScreen(),
),
],
),
));
}
Libraries
- plex_annotations/plex_annotation_builders/plex_annotation_builders
- plex_annotations/plex_annotations
- plex_annotations/plex_generators/plex_model_generators
- plex_annotations/plex_visitors/plex_model_visitor
- plex_assets
- plex_database/plex_collection
- plex_database/plex_database
- plex_database/plex_entity
- plex_database/plex_entity_collection
- plex_di/plex_dependency_injection
- plex_extensions
- plex_networking/plex_networking
- plex_package
- plex_route
- plex_rx/plex_rx
- plex_scanner
- plex_screens/plex_login_screen
- plex_screens/plex_screen
- plex_screens/plex_view
- plex_scrollview
- plex_signal_r/plex_signal_r
- plex_sp
- plex_theme
- plex_user
- plex_utils
- plex_utils/plex_date_utils
- plex_utils/plex_dimensions
- plex_utils/plex_material
- plex_utils/plex_messages
- plex_utils/plex_pair
- plex_utils/plex_printer
- plex_utils/plex_routing
- plex_utils/plex_utils
- plex_utils/plex_widgets
- plex_view_model/plex_view_model
- plex_widget
- plex_widgets/loading/plex_loader_v1
- plex_widgets/loading/plex_loader_v2
- plex_widgets/loading/plex_loading_enum
- plex_widgets/plex_adv_data_table
- plex_widgets/plex_data_table
- plex_widgets/plex_data_table_paginated
- plex_widgets/plex_date_picker_widget
- plex_widgets/plex_form
- plex_widgets/plex_form_field_widgets
- plex_widgets/plex_highlight_widget
- plex_widgets/plex_input_widget
- plex_widgets/plex_selection_list
- plex_widgets/plex_shimmer