├── .gitignore
├── LICENSE
├── README.md
├── dart_crm
├── lib
│ ├── blocs
│ │ ├── auth
│ │ │ ├── auth_bloc.dart
│ │ │ ├── settings_bloc.dart
│ │ │ └── signup_bloc.dart
│ │ ├── blogic
│ │ │ └── addressbook_bloc.dart
│ │ └── validators.dart
│ ├── main.dart
│ ├── models
│ │ └── datamodel.dart
│ ├── providers
│ │ ├── auth_provider.dart
│ │ └── auth_resources.dart
│ ├── shared
│ │ ├── custom_components.dart
│ │ ├── custom_forms.dart
│ │ ├── custom_style.dart
│ │ └── package
│ │ │ └── shared_preferences.dart
│ └── views
│ │ ├── addressbook
│ │ ├── addressbook-add.dart
│ │ ├── addressbook-edit.dart
│ │ └── addressbook.dart
│ │ ├── app.dart
│ │ └── auth
│ │ ├── login.dart
│ │ ├── profile.dart
│ │ ├── settings.dart
│ │ └── signup.dart
├── pubspec.yaml
└── web
│ ├── assets
│ ├── FontManifest.json
│ └── fonts
│ │ ├── Merriweather-Bold.ttf
│ │ └── OpenSans-Regular.ttf
│ ├── index.html
│ └── main.dart
└── nodejs_crm_server
├── .env
├── index.js
├── models
├── connector.js
└── dbconnection.js
├── package.json
└── validators
└── validate.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # Visual Studio Code related
19 | .vscode/
20 |
21 | # Flutter/Dart/Pub related
22 | **/doc/api/
23 | .dart_tool/
24 | .flutter-plugins
25 | .packages
26 | .pub-cache/
27 | .pub/
28 | /build/
29 |
30 | # Android related
31 | **/android/**/gradle-wrapper.jar
32 | **/android/.gradle
33 | **/android/captures/
34 | **/android/gradlew
35 | **/android/gradlew.bat
36 | **/android/local.properties
37 | **/android/**/GeneratedPluginRegistrant.java
38 |
39 | # iOS/XCode related
40 | **/ios/**/*.mode1v3
41 | **/ios/**/*.mode2v3
42 | **/ios/**/*.moved-aside
43 | **/ios/**/*.pbxuser
44 | **/ios/**/*.perspectivev3
45 | **/ios/**/*sync/
46 | **/ios/**/.sconsign.dblite
47 | **/ios/**/.tags*
48 | **/ios/**/.vagrant/
49 | **/ios/**/DerivedData/
50 | **/ios/**/Icon?
51 | **/ios/**/Pods/
52 | **/ios/**/.symlinks/
53 | **/ios/**/profile
54 | **/ios/**/xcuserdata
55 | **/ios/.generated/
56 | **/ios/Flutter/App.framework
57 | **/ios/Flutter/Flutter.framework
58 | **/ios/Flutter/Generated.xcconfig
59 | **/ios/Flutter/app.flx
60 | **/ios/Flutter/app.zip
61 | **/ios/Flutter/flutter_assets/
62 | **/ios/ServiceDefinitions.json
63 | **/ios/Runner/GeneratedPluginRegistrant.*
64 |
65 | # Exceptions to above rules.
66 | !**/ios/**/default.mode1v3
67 | !**/ios/**/default.mode2v3
68 | !**/ios/**/default.pbxuser
69 | !**/ios/**/default.perspectivev3
70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
71 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Amit Shukla
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ```diff
2 | - If you like this project, please consider giving it a star (*) and follow me at GitHub & YouTube.
3 | ```
4 | [
](https://youtube.com/AmitShukla_AI)
5 | [
](https://github.com/AmitXShukla)
6 | [
](https://medium.com/@Amit_Shukla)
7 | [
](https://twitter.com/ashuklax)
8 |
9 | # Flutter-MYSQL-CRM-app
10 | Flutter MYSQL CRM app - Free download with complete source code for iOS, Android, web
11 | # Do NOT Clone yet, this repository is work in progress and due 1.9 upgrade.
12 | # CRM
13 | Video Tutorials
14 |
15 | # Objective
16 | Build CRM for Small, Medium and Large Organizations
17 | Re-Write / Build new CRM GUI for existing ERP
18 | Convert Old Software to new App/UI (Desktop & Mobile) without changing database
19 | Migrate existing ERP to new platform
20 | Make an App for existing Oracle, PeopleSoft, SAP, or Siebel CRM or old custom software based ERP
21 |
22 | ** This app is NOT 100% complete yet because FLUTTER_WEB is not officially released.
23 | I am seeing performance issues with current FLUTTER_WEB version.
24 | So I'm building CRM app (complete version) in Angular, MYSQL (Option #2 mentioned below) for now.
25 | But I will update this CRM APP (this repository for Flutter/MYSQL) when FLUTTER WEB is released officially. **
26 |
27 | ## Technologies
28 | ## Option 1:
29 | FrontEnd: Flutter/DART
30 | Backend: NodeJS/REST API or DART
31 | Mobile: Flutter (iOS, Android)
32 | Database: ANY (Firebase, MongoDB, MYSQL, MS-SQL Server, MariaDB or Oracle)
33 |
34 | ## Option 2:
35 | FrontEnd: Angular, Project Clarity
36 | Backend: NodeJS, Express or PHP or JAVA or C# (DotNet) or GraphQL
37 | Mobile: Cordova (iOS, Android)
38 | Database: ANY (Firebase, MongoDB, MYSQL, MS-SQL Server, MariaDB or Oracle)
39 |
40 | # Pro - ERP
41 | HCM
42 | CRM
43 | SCM
44 | CRM, Supply Chain,
45 | Live Inventory
46 | Book Keeping
47 | Expense Management
48 | Assets management
49 |
50 |
51 | Manage CRM
52 |
53 | Users -> Admin
54 | Employee
55 | Manager
56 |
57 | AddressBook ->
58 | Global Addressbook
59 | Local Addressbook
60 |
61 | Marketing ->
62 | Campaigns
63 | Leads
64 | Oppurtunities
65 | Expenses
66 |
67 | Call Register ->
68 | Calls
69 | eMails
70 | enquiry
71 | visits
72 | Expenses
73 |
74 | Helpdesk -> Service tickets
75 | workorders
76 | Expenses
77 |
78 | Customer -> Invoice
79 | -> Sales Register
80 |
81 | Payables -> Setup Vendor
82 | -> Voucher for Vendor
83 | -> Pay Vendor
84 | --------------------
85 |
86 |
87 |
88 |
89 | Supply Chain & Inventory Management App (Redux or RxJs and/or React version)
90 | >> Setup
91 | Org/Company
92 | Vendor / Mfg
93 | Customer
94 |
95 | >> Register
96 | Requisition -> Generate Req and auto PO
97 | PO -> Request Inventory
98 | Receipt -> Receive Inventory
99 | Payables -> Setup Vendor
100 | -> Voucher for Vendor
101 | -> Pay Vendor
102 | Receivables-> Setup Customer
103 | -> Invoice for Customer
104 | -> Receive
105 | Sales Register
106 | # Reports
107 | Inventory Cycle Count
108 | Inventory Snapshot
109 | report - On Hand Inventory
110 | Payables -> Setup Vendor
111 | -> Voucher for Vendor
112 | -> Pay Vendor
113 |
--------------------------------------------------------------------------------
/dart_crm/lib/blocs/auth/auth_bloc.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:dart_crm/blocs/validators.dart';
4 | import 'package:dart_crm/providers/auth_resources.dart';
5 | import 'package:rxdart/rxdart.dart';
6 |
7 | class AuthBloc extends Object with Validators {
8 | final _emailController = BehaviorSubject();
9 | final _passwordController = BehaviorSubject();
10 |
11 | // API: Add data to stream
12 | Stream get email => _emailController.stream.transform(validateEmail);
13 | Stream get password =>
14 | _passwordController.stream.transform(validatePassword);
15 | Stream get submitValid =>
16 | Observable.combineLatest2(email, password, (e, p) => true);
17 |
18 | // API: change data
19 | Function(String) get changeEmail => _emailController.sink.add;
20 | Function(String) get changePassword => _passwordController.sink.add;
21 |
22 | // API: methods to perform actions
23 | submitData() {
24 | // TODO: Fix this later
25 | final validEmail = _emailController.value;
26 | final validPassword = _passwordController.value;
27 | print('Email is $validEmail, and password is $validPassword');
28 | }
29 |
30 | validateUserAuth() async {
31 | final validEmail = _emailController.value;
32 |
33 | final validPassword = _passwordController.value;
34 |
35 | if (validEmail != "" && validPassword != "") {
36 | return AuthRepository()
37 | .validateUser({"email": validEmail, "enc_password": validPassword});
38 | }
39 | }
40 |
41 | // API: dispose/cancel observables/subscriptions
42 | dispose() {
43 | _emailController.close();
44 | _passwordController.close();
45 | }
46 | }
47 |
48 | final authBloc = AuthBloc();
49 |
50 | ///**
51 | //import 'dart:async';
52 | //
53 | //import 'package:dart_crm/blocs/validators.dart';
54 | //import 'package:dart_crm/models/datamodel.dart';
55 | //import 'package:dart_crm/providers/auth_resources.dart';
56 | //import 'package:rxdart/rxdart.dart';
57 | //
58 | ////* Using a shortcut getter method on the class to create simpler and friendlier API for the class to provide access of a particular function on StreamController
59 | ////* Mixin can only be used on a class that extends from a base class, therefore, we are adding Bloc class that extends from the Object class
60 | ////NOTE: Or you can write "class Bloc extends Validators" since we don't really need to extend Bloc from a base class
61 | //class AuthBloc extends Object with Validators {
62 | // final _authRepository = AuthRepository();
63 | // final _authData = PublishSubject();
64 | //
65 | // //* "_" sets the instance variable to a private variable
66 | // //NOTE: By default, streams are created as "single-subscription stream", but in this case and in most cases, we need to create "broadcast stream"
67 | // //Note(con'd): because the email/password streams are consumed by the email/password fields as well as the combineLastest2 RxDart method
68 | // //Note:(con'd): because we need to merge these two streams as one and get the lastest streams of both that are valid to enable the button state
69 | // //Note:(con'd): Thus, below two streams are being consumed multiple times
70 | // //* original single-subscription stream
71 | // // final _emailController = StreamController();
72 | // // final _passwordController = StreamController();
73 | //
74 | // //* Broadcast stream
75 | // // final _emailController = StreamController.broadcast();
76 | // // final _passwordController = StreamController.broadcast();
77 | //
78 | // //* Replacing above Dart StreamController with RxDart BehaviourSubject (which is a broadcast stream by default)
79 | // //NOTE: We are leveraging the additional functionality from BehaviorSubject to go back in time and retrieve the lastest value of the streams for form submission
80 | // //NOTE: Dart StreamController doesn't have such functionality
81 | // final _emailController = BehaviorSubject();
82 | // final _passwordController = BehaviorSubject();
83 | //
84 | // // Add data to stream
85 | // Stream get email => _emailController.stream.transform(validateEmail);
86 | // Stream get password =>
87 | // _passwordController.stream.transform(validatePassword);
88 | //
89 | // Stream get submitValid =>
90 | // Observable.combineLatest2(email, password, (e, p) => true);
91 | //
92 | // // change data
93 | // Function(String) get changeEmail => _emailController.sink.add;
94 | // Function(String) get changePassword => _passwordController.sink.add;
95 | //
96 | // submit() {
97 | // final validEmail = _emailController.value;
98 | // final validPassword = _passwordController.value;
99 | //
100 | // print('Email is $validEmail, and password is $validPassword');
101 | // }
102 | //
103 | // validateUserAuth() async {
104 | // final validEmail = _emailController.value;
105 | // final validPassword = _passwordController.value;
106 | // UserModel userModel = await _authRepository.validateUser();
107 | // _authData.sink.add(userModel);
108 | // }
109 | //
110 | // dispose() {
111 | // _emailController.close();
112 | // _passwordController.close();
113 | // _authData.close();
114 | // }
115 | //}
116 | //
117 | ////Note: This creates a global instance of Bloc that's automatically exported and can be accessed anywhere in the app
118 | ////final bloc = Bloc();
119 | //
120 | ///** INCLUDE this implementation now
121 | // * import '../resources/repository.dart';
122 | // import 'package:rxdart/rxdart.dart';
123 | // import '../models/item_model.dart';
124 | //
125 | // class MoviesBloc {
126 | // final _repository = Repository();
127 | // final _moviesFetcher = PublishSubject();
128 | //
129 | // Observable get allMovies => _moviesFetcher.stream;
130 | //
131 | // fetchAllMovies() async {
132 | // ItemModel itemModel = await _repository.fetchAllMovies();
133 | // _moviesFetcher.sink.add(itemModel);
134 | // }
135 | //
136 | // dispose() {
137 | // _moviesFetcher.close();
138 | // }
139 | // }
140 | //
141 | // final bloc = MoviesBloc();
142 | //
143 | // * */
144 |
--------------------------------------------------------------------------------
/dart_crm/lib/blocs/auth/settings_bloc.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:dart_crm/blocs/validators.dart';
4 | import 'package:dart_crm/models/datamodel.dart';
5 | import 'package:dart_crm/providers/auth_resources.dart';
6 | import 'package:rxdart/rxdart.dart';
7 |
8 | class SettingsBloc extends Object with Validators {
9 | final _emailController = BehaviorSubject();
10 | final _nameController = BehaviorSubject();
11 | final _passwordController = BehaviorSubject();
12 |
13 | // API: Add data to stream
14 | Stream get email => _emailController.stream.transform(validateEmail);
15 | Stream get name => _nameController.stream.transform(validateText);
16 | Stream get password =>
17 | _passwordController.stream.transform(validatePassword);
18 | Stream get submitValid =>
19 | Observable.combineLatest3(email, name, password, (e, n, p) => true);
20 |
21 | // API: change data
22 | Function(String) get changeEmail => _emailController.sink.add;
23 | Function(String) get changeName => _nameController.sink.add;
24 | Function(String) get changePassword => _passwordController.sink.add;
25 |
26 | Future getUser() async {
27 | return await SettingsRepository().getUser();
28 | }
29 |
30 | Future setUser(formData) async {
31 | return SettingsRepository().setUser(formData);
32 | }
33 |
34 | // API: dispose/cancel observables/subscriptions
35 | dispose() {
36 | _emailController.close();
37 | _nameController.close();
38 | _passwordController.close();
39 | }
40 | }
41 |
42 | final settingsBloc = SettingsBloc();
43 |
44 | ///**
45 | //import 'dart:async';
46 | //
47 | //import 'package:dart_crm/blocs/validators.dart';
48 | //import 'package:dart_crm/models/datamodel.dart';
49 | //import 'package:dart_crm/providers/auth_resources.dart';
50 | //import 'package:rxdart/rxdart.dart';
51 | //
52 | ////* Using a shortcut getter method on the class to create simpler and friendlier API for the class to provide access of a particular function on StreamController
53 | ////* Mixin can only be used on a class that extends from a base class, therefore, we are adding Bloc class that extends from the Object class
54 | ////NOTE: Or you can write "class Bloc extends Validators" since we don't really need to extend Bloc from a base class
55 | //class AuthBloc extends Object with Validators {
56 | // final _authRepository = AuthRepository();
57 | // final _authData = PublishSubject();
58 | //
59 | // //* "_" sets the instance variable to a private variable
60 | // //NOTE: By default, streams are created as "single-subscription stream", but in this case and in most cases, we need to create "broadcast stream"
61 | // //Note(con'd): because the email/password streams are consumed by the email/password fields as well as the combineLastest2 RxDart method
62 | // //Note:(con'd): because we need to merge these two streams as one and get the lastest streams of both that are valid to enable the button state
63 | // //Note:(con'd): Thus, below two streams are being consumed multiple times
64 | // //* original single-subscription stream
65 | // // final _emailController = StreamController();
66 | // // final _passwordController = StreamController();
67 | //
68 | // //* Broadcast stream
69 | // // final _emailController = StreamController.broadcast();
70 | // // final _passwordController = StreamController.broadcast();
71 | //
72 | // //* Replacing above Dart StreamController with RxDart BehaviourSubject (which is a broadcast stream by default)
73 | // //NOTE: We are leveraging the additional functionality from BehaviorSubject to go back in time and retrieve the lastest value of the streams for form submission
74 | // //NOTE: Dart StreamController doesn't have such functionality
75 | // final _emailController = BehaviorSubject();
76 | // final _passwordController = BehaviorSubject();
77 | //
78 | // // Add data to stream
79 | // Stream get email => _emailController.stream.transform(validateEmail);
80 | // Stream get password =>
81 | // _passwordController.stream.transform(validatePassword);
82 | //
83 | // Stream get submitValid =>
84 | // Observable.combineLatest2(email, password, (e, p) => true);
85 | //
86 | // // change data
87 | // Function(String) get changeEmail => _emailController.sink.add;
88 | // Function(String) get changePassword => _passwordController.sink.add;
89 | //
90 | // submit() {
91 | // final validEmail = _emailController.value;
92 | // final validPassword = _passwordController.value;
93 | //
94 | // print('Email is $validEmail, and password is $validPassword');
95 | // }
96 | //
97 | // validateUserAuth() async {
98 | // final validEmail = _emailController.value;
99 | // final validPassword = _passwordController.value;
100 | // UserModel userModel = await _authRepository.validateUser();
101 | // _authData.sink.add(userModel);
102 | // }
103 | //
104 | // dispose() {
105 | // _emailController.close();
106 | // _passwordController.close();
107 | // _authData.close();
108 | // }
109 | //}
110 | //
111 | ////Note: This creates a global instance of Bloc that's automatically exported and can be accessed anywhere in the app
112 | ////final bloc = Bloc();
113 | //
114 | ///** INCLUDE this implementation now
115 | // * import '../resources/repository.dart';
116 | // import 'package:rxdart/rxdart.dart';
117 | // import '../models/item_model.dart';
118 | //
119 | // class MoviesBloc {
120 | // final _repository = Repository();
121 | // final _moviesFetcher = PublishSubject();
122 | //
123 | // Observable get allMovies => _moviesFetcher.stream;
124 | //
125 | // fetchAllMovies() async {
126 | // ItemModel itemModel = await _repository.fetchAllMovies();
127 | // _moviesFetcher.sink.add(itemModel);
128 | // }
129 | //
130 | // dispose() {
131 | // _moviesFetcher.close();
132 | // }
133 | // }
134 | //
135 | // final bloc = MoviesBloc();
136 | //
137 | // * */
138 |
--------------------------------------------------------------------------------
/dart_crm/lib/blocs/auth/signup_bloc.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:dart_crm/blocs/validators.dart';
4 | import 'package:dart_crm/providers/auth_resources.dart';
5 | import 'package:rxdart/rxdart.dart';
6 |
7 | class SignUpBloc extends Object with Validators {
8 | final _emailController = BehaviorSubject();
9 | final _nameController = BehaviorSubject();
10 | final _passwordController = BehaviorSubject();
11 |
12 | // API: Add data to stream
13 | Stream get email => _emailController.stream.transform(validateEmail);
14 | Stream get name => _nameController.stream.transform(validateText);
15 | Stream get password =>
16 | _passwordController.stream.transform(validatePassword);
17 | Stream get submitValid =>
18 | Observable.combineLatest3(email, name, password, (e, n, p) => true);
19 |
20 | // API: change data
21 | Function(String) get changeEmail => _emailController.sink.add;
22 | Function(String) get changeName => _nameController.sink.add;
23 | Function(String) get changePassword => _passwordController.sink.add;
24 |
25 | signupUser() async {
26 | final validEmail = _emailController.value;
27 | final validName = _nameController.value;
28 | final validPassword = _passwordController.value;
29 |
30 | if (validEmail != "" && validName != "" && validPassword != "") {
31 | return SignupRepository().createUser({
32 | "email": validEmail,
33 | "name": validName,
34 | "enc_password": validPassword
35 | });
36 | }
37 | }
38 |
39 | // API: dispose/cancel observables/subscriptions
40 | dispose() {
41 | _emailController.close();
42 | _nameController.close();
43 | _passwordController.close();
44 | }
45 | }
46 |
47 | final signupBloc = SignUpBloc();
48 |
--------------------------------------------------------------------------------
/dart_crm/lib/blocs/blogic/addressbook_bloc.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:dart_crm/blocs/validators.dart';
4 | import 'package:dart_crm/models/datamodel.dart';
5 | import 'package:dart_crm/providers/auth_resources.dart';
6 |
7 | class AddressBookBloc extends Object with Validators {
8 | Future getUser() async {
9 | return await SettingsRepository().getUser();
10 | }
11 |
12 | Future getData(String _id, String srchTxt) async {
13 | return await SettingsRepository()
14 | .getData({"table_name": "addressbook", "_id": _id, "srchTxt": srchTxt});
15 | }
16 |
17 | Future setData(formData) async {
18 | return SettingsRepository().setData(formData);
19 | }
20 |
21 | // API: dispose/cancel observables/subscriptions
22 | dispose() {}
23 | }
24 |
25 | final addressBookBloc = AddressBookBloc();
26 |
--------------------------------------------------------------------------------
/dart_crm/lib/blocs/validators.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | class Validators {
4 | final validateEmail =
5 | StreamTransformer.fromHandlers(handleData: (email, sink) {
6 | Pattern pattern =
7 | r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
8 | RegExp regex = new RegExp(pattern);
9 | if (regex.hasMatch(email))
10 | sink.add(email);
11 | else
12 | sink.addError('Please enter a valid email');
13 | });
14 |
15 | final validatePassword = StreamTransformer.fromHandlers(
16 | handleData: (password, sink) {
17 | if (password.length > 4) {
18 | sink.add(password);
19 | } else {
20 | sink.addError('Invalid password, please enter more than 4 characters');
21 | }
22 | });
23 |
24 | final validateText =
25 | StreamTransformer.fromHandlers(handleData: (name, sink) {
26 | if (name.length > 4) {
27 | sink.add(name);
28 | } else {
29 | sink.addError('Invalid Text, please enter more than 4 characters');
30 | }
31 | });
32 |
33 | bool validateFormText(String txt) {
34 | //WhitelistingTextInputFormatter(new RegExp(r'^[()\d -]{1,15}$')),
35 | if (txt.isEmpty) return true;
36 | return false;
37 | }
38 |
39 | bool isValidEmail(String input) {
40 | final RegExp regex = new RegExp(
41 | r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$");
42 | return regex.hasMatch(input);
43 | }
44 |
45 | bool isValidPhoneNumber(String input) {
46 | final RegExp regex = new RegExp(r'^\(\d\d\d\)\d\d\d\-\d\d\d\d$');
47 | return regex.hasMatch(input);
48 | }
49 | }
50 |
51 | final validatorBloc = Validators();
52 |
--------------------------------------------------------------------------------
/dart_crm/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_web/material.dart';
2 | import './views/app.dart';
3 |
4 | void main() {
5 | runApp(App());
6 | }
7 |
--------------------------------------------------------------------------------
/dart_crm/lib/models/datamodel.dart:
--------------------------------------------------------------------------------
1 | class DBDataModel {
2 | final int num_rows;
3 | final bool error;
4 | final String message;
5 | final List data;
6 | const DBDataModel({this.num_rows, this.error, this.message, this.data});
7 | factory DBDataModel.fromJson(Map json) {
8 | return DBDataModel(
9 | num_rows: json['num_rows'],
10 | error: json['error'],
11 | message: json['message'],
12 | data: json['data']
13 | .map((value) => new UserModel.fromJson(value))
14 | .toList());
15 | }
16 | }
17 |
18 | class AddressDataModel {
19 | final int num_rows;
20 | final bool error;
21 | final String message;
22 | final List data;
23 | const AddressDataModel({this.num_rows, this.error, this.message, this.data});
24 | factory AddressDataModel.fromJson(Map json) {
25 | return AddressDataModel(
26 | num_rows: json['num_rows'],
27 | error: json['error'],
28 | message: json['message'],
29 | data: json['data']
30 | .map((value) => new AddressBookModel.fromJson(value))
31 | .toList());
32 | }
33 | }
34 |
35 | class UserModel {
36 | final String userid;
37 | final String name;
38 | final String jwttoken;
39 | final String createdAt;
40 | final String updatedAt;
41 | final String role;
42 |
43 | const UserModel(
44 | {this.userid,
45 | this.name,
46 | this.jwttoken,
47 | this.createdAt,
48 | this.updatedAt,
49 | this.role});
50 |
51 | factory UserModel.fromJson(Map json) {
52 | return UserModel(
53 | userid: json['userid'],
54 | name: json['name'],
55 | jwttoken: json['jwttoken'],
56 | createdAt: json['createdAt'],
57 | updatedAt: json['updatedAt'],
58 | role: json['role']);
59 | }
60 | factory UserModel.toJson(Map json) {
61 | return UserModel(
62 | userid: json['userid'],
63 | name: json['name'],
64 | jwttoken: json['jwttoken'],
65 | );
66 | }
67 | }
68 |
69 | class AddressBookModel {
70 | final int addressid;
71 | final String first_name;
72 | final String middle_name;
73 | final String last_name;
74 | final String address;
75 | final String city;
76 | final String country;
77 | final String zip_code;
78 | final String emailid1;
79 | final String emailid2;
80 | final String phone1;
81 | final String phone2;
82 | final String createdAt;
83 | final String updatedAt;
84 |
85 | const AddressBookModel(
86 | {this.addressid,
87 | this.first_name,
88 | this.middle_name,
89 | this.last_name,
90 | this.address,
91 | this.city,
92 | this.country,
93 | this.zip_code,
94 | this.emailid1,
95 | this.emailid2,
96 | this.phone1,
97 | this.phone2,
98 | this.createdAt,
99 | this.updatedAt});
100 |
101 | factory AddressBookModel.fromJson(Map json) {
102 | return AddressBookModel(
103 | addressid: json['addressid'],
104 | first_name: json['first_name'],
105 | middle_name: json['middle_name'],
106 | last_name: json['last_name'],
107 | address: json['address'],
108 | city: json['city'],
109 | country: json['country'],
110 | zip_code: json['zip_code'],
111 | emailid1: json['emailid1'],
112 | emailid2: json['emailid2'],
113 | phone1: json['phone1'],
114 | phone2: json['phone2'],
115 | createdAt: json['createdAt'],
116 | updatedAt: json['updatedAt']);
117 | }
118 | factory AddressBookModel.toJson(Map json) {
119 | return AddressBookModel(
120 | addressid: json['addressid'],
121 | first_name: json['first_name'],
122 | middle_name: json['middle_name'],
123 | last_name: json['last_name'],
124 | address: json['address'],
125 | city: json['city'],
126 | country: json['country'],
127 | zip_code: json['zip_code'],
128 | emailid1: json['emailid1'],
129 | emailid2: json['emailid2'],
130 | phone1: json['phone1'],
131 | phone2: json['phone2'],
132 | createdAt: json['createdAt'],
133 | updatedAt: json['updatedAt']);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/dart_crm/lib/providers/auth_provider.dart:
--------------------------------------------------------------------------------
1 | //import 'package:dart_crm/blocs/auth_bloc.dart';
2 | //import 'package:flutter_web/material.dart';
3 | //
4 | //class Provider extends InheritedWidget {
5 | // final bloc = Bloc();
6 | //
7 | // Provider({Key key, Widget child}) : super(key: key, child: child);
8 | //
9 | // bool updateShouldNotify(_) => true;
10 | //
11 | // static Bloc of(BuildContext context) {
12 | // //* What it does is through the "of" function, it looks through the context of a widget from the deepest in the widget tree
13 | // //* and it keeps travelling up to each widget's parent's context until it finds a "Provider" widget
14 | // //* and performs the type conversion to Provider through "as Provider" and then access the Provider's bloc instance variable
15 | // return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
16 | // }
17 | //}
18 | import 'dart:async';
19 | import 'dart:convert';
20 |
21 | import 'package:dart_crm/models/datamodel.dart';
22 | import 'package:http/http.dart' show Client;
23 |
24 | class UserAuthApiProvider {
25 | var fakeJsonResponse_1 = """{
26 | "num_rows": 1,
27 | "error": false,
28 | "message": "Operation Successful.",
29 | "data": [
30 | {
31 | "userid": "amit@elishconsulting.com",
32 | "name": "Amit Shukla",
33 | "role": "Admin",
34 | "jwttoken": "abcd",
35 | "createdAt": "abcd",
36 | "updatedAt": "abcd"
37 | }
38 | ]
39 | }""";
40 | var serverFailed = """{
41 | "num_rows": 0,
42 | "error": true,
43 | "message": "Server Error. Possible Broken Network. Please send an email to info@elishconsulting.com",
44 | "data": [
45 | {
46 | "userid": "",
47 | "name": "",
48 | "role": "",
49 | "jwttoken": "",
50 | "createdAt": "",
51 | "updatedAt": ""
52 | }
53 | ]
54 | }""";
55 | Client client = Client();
56 | final _baseUrl = "http://localhost:3000";
57 |
58 | // let token = localStorage.getItem('token') ? localStorage.getItem('token') : "abcd";
59 | // let httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'token': token }) };
60 |
61 | Future validateUserAuth(formData) async {
62 | try {
63 | final response = await client.post("$_baseUrl/login",
64 | headers: {
65 | //HttpHeaders.authorizationHeader: ["token" : "Basic your_api_token_here"]
66 | "token": "invalid_token"
67 | },
68 | body: formData);
69 | if (response.statusCode == 200) {
70 | // If the call to the server was successful, parse the JSON
71 | print(response.body);
72 | return await DBDataModel.fromJson(json.decode(response.body));
73 | }
74 | } catch (_) {
75 | return await DBDataModel.fromJson(json.decode(serverFailed));
76 | }
77 | return await DBDataModel.fromJson(json.decode(serverFailed));
78 | }
79 |
80 | Future createUser(formData) async {
81 | try {
82 | final response = await client.post("$_baseUrl/signup",
83 | headers: {
84 | //HttpHeaders.authorizationHeader: "Basic your_api_token_here"
85 | "token": "invalid_token"
86 | },
87 | body: formData);
88 | if (response.statusCode == 200) {
89 | // If the call to the server was successful, parse the JSON
90 | return await DBDataModel.fromJson(json.decode(response.body));
91 | }
92 | } catch (_) {
93 | return await DBDataModel.fromJson(json.decode(serverFailed));
94 | }
95 | return await DBDataModel.fromJson(json.decode(serverFailed));
96 | }
97 |
98 | Future getUser() async {
99 | final formData = {};
100 | // final token = "invalid_token";
101 | final token =
102 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFtaXRAeWFob28uY29tIiwiaWF0IjoxNTYzODUwNDIwLCJleHAiOjE1NjY0NDI0MjB9.7TAxrqaRNwIEMQH-Qv51BLk3Zy_eYe3KtGN_Id1hVA4";
103 | // FLUTTER_WEB version is not working to store at present
104 | // TODO: enable this for android/ios version
105 | // final prefs = await SharedPreferences.getInstance();
106 | // final token = prefs.getInt('token') ?? "invalid_token";
107 | try {
108 | final response = await client.post("$_baseUrl/getuser",
109 | headers: {"token": token}, body: formData);
110 | if (response.statusCode == 200) {
111 | // If the call to the server was successful, parse the JSON
112 | return await DBDataModel.fromJson(json.decode(response.body));
113 | }
114 | } catch (_) {
115 | return await DBDataModel.fromJson(json.decode(serverFailed));
116 | }
117 | return await DBDataModel.fromJson(json.decode(serverFailed));
118 | }
119 |
120 | Future setUser(formData) async {
121 | final formData2 = formData;
122 | //final token = "invalid_token";
123 | final token =
124 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFtaXRAeWFob28uY29tIiwiaWF0IjoxNTYzNzcxMDEyLCJleHAiOjE1NjYzNjMwMTJ9.c39YqxKYtdqi199Ivh_-7LPzTe8BC21xWtvcqv0TTck";
125 | // FLUTTER_WEB version is not working to store at present
126 | // TODO: enable this for android/ios version
127 | // final prefs = await SharedPreferences.getInstance();
128 | // final token = prefs.getInt('token') ?? "invalid_token";
129 | try {
130 | //print(formData.userid);
131 | final response = await client.post("$_baseUrl/setuser",
132 | headers: {"token": token}, body: formData2);
133 | if (response.statusCode == 200) {
134 | // If the call to the server was successful, parse the JSON
135 | return await DBDataModel.fromJson(json.decode(response.body));
136 | }
137 | } catch (_) {
138 | return await DBDataModel.fromJson(json.decode(serverFailed));
139 | }
140 | return await DBDataModel.fromJson(json.decode(serverFailed));
141 | }
142 |
143 | Future setData(formData) async {
144 | final formData2 = formData;
145 | //final token = "invalid_token";
146 | final token =
147 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFtaXRAeWFob28uY29tIiwiaWF0IjoxNTYzNzcxMDEyLCJleHAiOjE1NjYzNjMwMTJ9.c39YqxKYtdqi199Ivh_-7LPzTe8BC21xWtvcqv0TTck";
148 | // FLUTTER_WEB version is not working to store at present
149 | // TODO: enable this for android/ios version
150 | // final prefs = await SharedPreferences.getInstance();
151 | // final token = prefs.getInt('token') ?? "invalid_token";
152 | try {
153 | //print(formData.userid);
154 | final response = await client.post("$_baseUrl/setdata",
155 | headers: {"token": token}, body: formData2);
156 | if (response.statusCode == 200) {
157 | // If the call to the server was successful, parse the JSON
158 | return await DBDataModel.fromJson(json.decode(response.body));
159 | }
160 | } catch (_) {
161 | return await DBDataModel.fromJson(json.decode(serverFailed));
162 | }
163 | return await DBDataModel.fromJson(json.decode(serverFailed));
164 | }
165 |
166 | Future getData(formData) async {
167 | final formData2 = formData;
168 | //final token = "invalid_token";
169 | final token =
170 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFtaXRAeWFob28uY29tIiwiaWF0IjoxNTYzNzcxMDEyLCJleHAiOjE1NjYzNjMwMTJ9.c39YqxKYtdqi199Ivh_-7LPzTe8BC21xWtvcqv0TTck";
171 | // FLUTTER_WEB version is not working to store at present
172 | // TODO: enable this for android/ios version
173 | // final prefs = await SharedPreferences.getInstance();
174 | // final token = prefs.getInt('token') ?? "invalid_token";
175 | try {
176 | //print(formData.userid);
177 | final response = await client.post("$_baseUrl/getdata",
178 | headers: {"token": token}, body: formData2);
179 | if (response.statusCode == 200) {
180 | // If the call to the server was successful, parse the JSON
181 | // print(response.body);
182 | return await AddressDataModel.fromJson(json.decode(response.body));
183 | }
184 | } catch (_) {
185 | return await AddressDataModel.fromJson(json.decode(serverFailed));
186 | }
187 | return await AddressDataModel.fromJson(json.decode(serverFailed));
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/dart_crm/lib/providers/auth_resources.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'auth_provider.dart';
4 |
5 | class AuthRepository {
6 | final userAuthApiProvider = UserAuthApiProvider();
7 | Future validateUser(formData) =>
8 | userAuthApiProvider.validateUserAuth(formData);
9 | }
10 |
11 | class SignupRepository {
12 | final userAuthApiProvider = UserAuthApiProvider();
13 | Future createUser(formData) => userAuthApiProvider.createUser(formData);
14 | }
15 |
16 | class SettingsRepository {
17 | final userAuthApiProvider = UserAuthApiProvider();
18 | //Future getUser(formData) => userAuthApiProvider.getUser(formData);
19 | Future getUser() => userAuthApiProvider.getUser();
20 | Future setUser(formData) => userAuthApiProvider.setUser(formData);
21 | Future getData(formData) => userAuthApiProvider.getData(formData);
22 | Future setData(formData) => userAuthApiProvider.setData(formData);
23 | }
24 |
--------------------------------------------------------------------------------
/dart_crm/lib/shared/custom_components.dart:
--------------------------------------------------------------------------------
1 | import 'package:dart_crm/shared/custom_style.dart';
2 | import 'package:flutter_web/material.dart';
3 |
4 | class CustomSpinner extends StatelessWidget {
5 | final bool toggleSpinner;
6 | const CustomSpinner({Key key, this.toggleSpinner}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Center(child: toggleSpinner ? CircularProgressIndicator() : null);
11 | }
12 | }
13 |
14 | class CustomMessage extends StatelessWidget {
15 | final bool toggleMessage;
16 | final toggleMessageType;
17 | final String toggleMessageTxt;
18 | const CustomMessage(
19 | {Key key,
20 | this.toggleMessage,
21 | this.toggleMessageType,
22 | this.toggleMessageTxt})
23 | : super(key: key);
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | return Center(
28 | child: toggleMessage
29 | ? Text(toggleMessageTxt,
30 | style: toggleMessageType == cMessageType.error.toString()
31 | ? cErrorText
32 | : cSuccessText)
33 | : null);
34 | }
35 | }
36 |
37 | class CustomDrawer extends StatelessWidget {
38 | //final bool toggleSpinner;
39 | const CustomDrawer({Key key}) : super(key: key);
40 |
41 | @override
42 | Widget build(BuildContext context) {
43 | return Drawer(
44 | semanticLabel: cLabel,
45 | // Add a ListView to the drawer. This ensures the user can scroll
46 | // through the options in the drawer if there isn't enough vertical
47 | // space to fit everything.
48 | child: ListView(
49 | // Important: Remove any padding from the ListView.
50 | padding: EdgeInsets.all(4.0),
51 | children: [
52 | UserAccountsDrawerHeader(
53 | accountName: Text(cAppTitle),
54 | accountEmail: Text(cEmailID),
55 | currentAccountPicture: CircleAvatar(
56 | backgroundImage: NetworkImage(cSampleImage),
57 | ),
58 | ),
59 | SizedBox(height: 10),
60 | ListTile(
61 | leading: Icon(Icons.book),
62 | title: Text(
63 | "AddressBook",
64 | style: cNavText,
65 | ),
66 | onTap: () => {
67 | Navigator.pushReplacementNamed(
68 | context,
69 | '/addressbook',
70 | )
71 | },
72 | ),
73 | ListTile(
74 | leading: Icon(Icons.business),
75 | title: Text(
76 | "Marketing",
77 | style: cNavText,
78 | ),
79 | onTap: null,
80 | subtitle: Text('Manage Campaigns, Leads & Opportunities.'),
81 | trailing: Icon(Icons.more_vert),
82 | isThreeLine: true,
83 | ),
84 | ListTile(
85 | leading: Icon(Icons.call),
86 | title: Text(
87 | "Call Register",
88 | style: cNavText,
89 | ),
90 | onTap: null,
91 | subtitle: Text('Manage Calls, eMails, enquiry, & visits'),
92 | trailing: Icon(Icons.more_vert),
93 | isThreeLine: true,
94 | ),
95 | ListTile(
96 | leading: Icon(Icons.dashboard),
97 | title: Text(
98 | "Customer",
99 | style: cNavText,
100 | ),
101 | onTap: null,
102 | subtitle: Text('Bills, Invoices & Sales register'),
103 | trailing: Icon(Icons.more_vert),
104 | isThreeLine: true,
105 | ),
106 | ListTile(
107 | leading: Icon(Icons.sms),
108 | title: Text(
109 | "HelpDesk",
110 | style: cNavText,
111 | ),
112 | onTap: null,
113 | subtitle: Text('Service Tickets, Workorder'),
114 | trailing: Icon(Icons.more_vert),
115 | isThreeLine: true,
116 | ),
117 | ListTile(
118 | leading: Icon(Icons.satellite),
119 | title: Text(
120 | "Vendors",
121 | style: cNavText,
122 | ),
123 | onTap: null,
124 | subtitle: Text('Vouchers, Bills, Invoices'),
125 | trailing: Icon(Icons.more_vert),
126 | isThreeLine: true,
127 | ),
128 | ListTile(
129 | leading: Icon(Icons.settings),
130 | title: Text(
131 | "Admin",
132 | style: cNavText,
133 | ),
134 | onTap: null,
135 | ),
136 | RaisedButton(
137 | child: Text('Logout'),
138 | color: Colors.blue,
139 | onPressed: () {
140 | Navigator.pushReplacementNamed(
141 | context,
142 | '/',
143 | );
144 | },
145 | ),
146 | ],
147 | ),
148 | );
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/dart_crm/lib/shared/custom_forms.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_web/material.dart';
2 |
3 | class CustomFormRoundedTxt extends StatelessWidget {
4 | final Stream streamBloc;
5 | final bool obscureTxt;
6 | final onChangeTxt;
7 | final iconTxt;
8 | final hintTxt;
9 | final labelTxt;
10 | const CustomFormRoundedTxt(
11 | {Key key,
12 | this.streamBloc,
13 | this.obscureTxt,
14 | this.onChangeTxt,
15 | this.iconTxt,
16 | this.hintTxt,
17 | this.labelTxt})
18 | : super(key: key);
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return StreamBuilder(
23 | stream: streamBloc,
24 | builder: (context, snapshot) {
25 | return Container(
26 | width: 300.0,
27 | margin: EdgeInsets.only(top: 25.0),
28 | child: TextField(
29 | cursorColor: Colors.blueAccent,
30 | maxLength: 50,
31 | obscureText: obscureTxt,
32 | onChanged: onChangeTxt,
33 | decoration: InputDecoration(
34 | icon: iconTxt,
35 | border: OutlineInputBorder(
36 | borderRadius: BorderRadius.circular(16.0)),
37 | hintText: hintTxt,
38 | labelText: labelTxt,
39 | errorText: snapshot.error,
40 | ),
41 | ));
42 | });
43 | }
44 | }
45 |
46 | class CustomFormTxt extends StatelessWidget {
47 | final Stream streamBloc;
48 | final int boxLength;
49 | final obscureTxt;
50 | final onChangeTxt;
51 | final iconTxt;
52 | final hintTxt;
53 | final labelTxt;
54 | const CustomFormTxt(
55 | {Key key,
56 | this.streamBloc,
57 | this.boxLength,
58 | this.obscureTxt,
59 | this.onChangeTxt,
60 | this.iconTxt,
61 | this.hintTxt,
62 | this.labelTxt})
63 | : super(key: key);
64 |
65 | @override
66 | Widget build(BuildContext context) {
67 | return StreamBuilder(
68 | stream: streamBloc,
69 | builder: (context, snapshot) {
70 | return Container(
71 | width: 300.0,
72 | margin: EdgeInsets.only(top: 5.0),
73 | child: TextField(
74 | cursorColor: Colors.blueAccent,
75 | maxLength: boxLength,
76 | obscureText: obscureTxt,
77 | onChanged: onChangeTxt,
78 | decoration: InputDecoration(
79 | icon: iconTxt,
80 | hintText: hintTxt,
81 | labelText: labelTxt,
82 | errorText: snapshot.error,
83 | ),
84 | ));
85 | });
86 | }
87 | }
88 |
89 | class CustomFormDataTxt extends StatelessWidget {
90 | final String dbData;
91 | final bool isEnabled;
92 | final Stream streamBloc;
93 | final int boxLength;
94 | final obscureTxt;
95 | final onChangeTxt;
96 | final iconTxt;
97 | final hintTxt;
98 | final labelTxt;
99 | const CustomFormDataTxt(
100 | {Key key,
101 | this.dbData,
102 | this.isEnabled,
103 | this.streamBloc,
104 | this.boxLength,
105 | this.obscureTxt,
106 | this.onChangeTxt,
107 | this.iconTxt,
108 | this.hintTxt,
109 | this.labelTxt})
110 | : super(key: key);
111 |
112 | @override
113 | Widget build(BuildContext context) {
114 | var txtData = TextEditingController();
115 | txtData.text = dbData;
116 | return StreamBuilder(
117 | stream: streamBloc,
118 | builder: (context, snapshot) {
119 | return Container(
120 | width: 300.0,
121 | margin: EdgeInsets.only(top: 5.0),
122 | child: TextField(
123 | controller: txtData,
124 | cursorColor: Colors.blueAccent,
125 | enabled: isEnabled,
126 | maxLength: boxLength,
127 | obscureText: obscureTxt,
128 | onChanged: onChangeTxt,
129 | decoration: InputDecoration(
130 | icon: iconTxt,
131 | hintText: hintTxt,
132 | labelText: labelTxt,
133 | errorText: snapshot.error,
134 | ),
135 | ));
136 | });
137 | }
138 | }
139 |
140 | class CustomFormData1Txt extends StatelessWidget {
141 | final bool isEnabled;
142 | final Stream streamBloc;
143 | final int boxLength;
144 | final obscureTxt;
145 | final onChangeTxt;
146 | final iconTxt;
147 | final hintTxt;
148 | final labelTxt;
149 | const CustomFormData1Txt(
150 | {Key key,
151 | this.isEnabled,
152 | this.streamBloc,
153 | this.boxLength,
154 | this.obscureTxt,
155 | this.onChangeTxt,
156 | this.iconTxt,
157 | this.hintTxt,
158 | this.labelTxt})
159 | : super(key: key);
160 |
161 | @override
162 | Widget build(BuildContext context) {
163 | return StreamBuilder(
164 | stream: streamBloc,
165 | builder: (context, snapshot) {
166 | return Container(
167 | width: 300.0,
168 | margin: EdgeInsets.only(top: 5.0),
169 | child: TextField(
170 | cursorColor: Colors.blueAccent,
171 | enabled: isEnabled,
172 | maxLength: boxLength,
173 | obscureText: obscureTxt,
174 | onChanged: onChangeTxt,
175 | decoration: InputDecoration(
176 | icon: iconTxt,
177 | hintText: hintTxt,
178 | labelText: labelTxt,
179 | errorText: snapshot.error,
180 | ),
181 | ));
182 | });
183 | }
184 | }
185 |
186 | class CustomFormTextBox extends StatelessWidget {
187 | final String dbData;
188 | final int boxLength;
189 | final bool isEnabled;
190 | final obscureTxt;
191 | final iconTxt;
192 | final hintTxt;
193 | final labelTxt;
194 | const CustomFormTextBox(
195 | {Key key,
196 | this.dbData,
197 | this.boxLength,
198 | this.isEnabled,
199 | this.obscureTxt,
200 | this.iconTxt,
201 | this.hintTxt,
202 | this.labelTxt})
203 | : super(key: key);
204 |
205 | @override
206 | Widget build(BuildContext context) {
207 | var txt = TextEditingController();
208 | txt.text = dbData;
209 | return Container(
210 | width: 300.0,
211 | margin: EdgeInsets.only(top: 5.0),
212 | child: TextFormField(
213 | controller: txt,
214 | enabled: isEnabled,
215 | cursorColor: Colors.blueAccent,
216 | maxLength: boxLength,
217 | obscureText: obscureTxt,
218 | decoration: InputDecoration(
219 | icon: iconTxt,
220 | hintText: hintTxt,
221 | labelText: labelTxt,
222 | ),
223 | ),
224 | );
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/dart_crm/lib/shared/custom_style.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_web/material.dart';
2 |
3 | const cAppTitle = "CRM";
4 | const cSettingsTitle = "Settings";
5 | const cAddressBookTitle = "Address Book";
6 | const cAddressBookAddTitle = "Add Address Book";
7 | const cAddressBookEditTitle = "Edit Address Book";
8 | const cSignUpTitle = "Sign up";
9 |
10 | enum cMessageType { error, success }
11 |
12 | const cNavText = TextStyle(
13 | color: Colors.blueAccent,
14 | fontSize: 16.0,
15 | fontWeight: FontWeight.w500,
16 | fontStyle: FontStyle.normal);
17 | const cNavRightText = TextStyle(
18 | color: Colors.blueAccent,
19 | fontSize: 14.0,
20 | fontWeight: FontWeight.w500,
21 | fontStyle: FontStyle.normal);
22 |
23 | const cEmailID = "info@elishconsulting.com";
24 | const cLabel = "Navigation Menu";
25 | const cSampleImage =
26 | "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRJIzlrP5Fm5juFKR3saDL1rYDOV32y5IPF3UWC0CbIEhDgayJzrw";
27 |
28 | const cBodyText = TextStyle(
29 | fontWeight: FontWeight.w400,
30 | color: Colors.blueGrey,
31 | );
32 | const cErrorText = TextStyle(
33 | fontWeight: FontWeight.w400,
34 | color: Colors.red,
35 | );
36 | const cWarnText = TextStyle(
37 | fontWeight: FontWeight.w400,
38 | color: Colors.yellow,
39 | );
40 | const cSuccessText = TextStyle(
41 | fontWeight: FontWeight.w400,
42 | color: Colors.green,
43 | );
44 |
45 | const cHeaderText = TextStyle(
46 | color: Colors.blueAccent,
47 | fontSize: 20.0,
48 | fontWeight: FontWeight.w500,
49 | fontStyle: FontStyle.normal);
50 |
51 | const cHeaderWhiteText = TextStyle(
52 | color: Colors.white,
53 | fontSize: 20.0,
54 | fontWeight: FontWeight.w500,
55 | fontStyle: FontStyle.normal);
56 |
57 | const cHeaderDarkText = TextStyle(
58 | color: Colors.blueGrey,
59 | fontSize: 20.0,
60 | fontWeight: FontWeight.w500,
61 | fontStyle: FontStyle.normal);
62 |
63 | var cThemeData = ThemeData(
64 | primaryColor: Colors.blue,
65 | //primarySwatch: Colors.white,
66 | buttonColor: Colors.blue,
67 | backgroundColor: Colors.white,
68 | buttonTheme: const ButtonThemeData(textTheme: ButtonTextTheme.primary),
69 | );
70 |
--------------------------------------------------------------------------------
/dart_crm/lib/shared/package/shared_preferences.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | import 'dart:async';
6 |
7 | import 'package:flutter_web/services.dart';
8 | import 'package:meta/meta.dart';
9 |
10 | const MethodChannel _kChannel =
11 | MethodChannel('plugins.flutter.io/shared_preferences');
12 |
13 | /// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing
14 | /// a persistent store for simple data.
15 | ///
16 | /// Data is persisted to disk asynchronously.
17 | class SharedPreferences {
18 | SharedPreferences._(this._preferenceCache);
19 |
20 | static const String _prefix = 'flutter.';
21 | static SharedPreferences _instance;
22 | static Future getInstance() async {
23 | if (_instance == null) {
24 | final Map preferencesMap =
25 | await _getSharedPreferencesMap();
26 | _instance = SharedPreferences._(preferencesMap);
27 | }
28 | return _instance;
29 | }
30 |
31 | /// The cache that holds all preferences.
32 | ///
33 | /// It is instantiated to the current state of the SharedPreferences or
34 | /// NSUserDefaults object and then kept in sync via setter methods in this
35 | /// class.
36 | ///
37 | /// It is NOT guaranteed that this cache and the device prefs will remain
38 | /// in sync since the setter method might fail for any reason.
39 | final Map _preferenceCache;
40 |
41 | /// Returns all keys in the persistent storage.
42 | Set getKeys() => Set.from(_preferenceCache.keys);
43 |
44 | /// Reads a value of any type from persistent storage.
45 | dynamic get(String key) => _preferenceCache[key];
46 |
47 | /// Reads a value from persistent storage, throwing an exception if it's not a
48 | /// bool.
49 | bool getBool(String key) => _preferenceCache[key];
50 |
51 | /// Reads a value from persistent storage, throwing an exception if it's not
52 | /// an int.
53 | int getInt(String key) => _preferenceCache[key];
54 |
55 | /// Reads a value from persistent storage, throwing an exception if it's not a
56 | /// double.
57 | double getDouble(String key) => _preferenceCache[key];
58 |
59 | /// Reads a value from persistent storage, throwing an exception if it's not a
60 | /// String.
61 | String getString(String key) => _preferenceCache[key];
62 |
63 | /// Returns true if persistent storage the contains the given [key].
64 | bool containsKey(String key) => _preferenceCache.containsKey(key);
65 |
66 | /// Reads a set of string values from persistent storage, throwing an
67 | /// exception if it's not a string set.
68 | List getStringList(String key) {
69 | List