mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 05:15:42 +00:00 
			
		
		
		
	Refactoring / fixing
This commit is contained in:
		
							
								
								
									
										30
									
								
								lib/api.dart
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								lib/api.dart
									
									
									
									
									
								
							| @@ -177,6 +177,8 @@ class InvenTreeAPI { | ||||
|      * e.g. http://127.0.0.1:8000 | ||||
|      */ | ||||
|  | ||||
|     if (profile == null) return false; | ||||
|  | ||||
|     String address = profile.server.trim(); | ||||
|     String username = profile.username.trim(); | ||||
|     String password = profile.password.trim(); | ||||
| @@ -212,10 +214,13 @@ class InvenTreeAPI { | ||||
|       print("Error connecting to server: ${error.toString()}"); | ||||
|  | ||||
|       if (error is SocketException) { | ||||
|         showServerError(context, "Connection Refused"); | ||||
|         print("Error: socket exception: ${error.toString()}"); | ||||
|         // TODO - Display error dialog!! | ||||
|         //showServerError(context, "Connection Refused"); | ||||
|         return null; | ||||
|       } else if (error is TimeoutException) { | ||||
|         showTimeoutDialog(context); | ||||
|         // TODO - Display timeout dialog here | ||||
|         //showTimeoutDialog(context); | ||||
|         return null; | ||||
|       } else { | ||||
|         // Unknown error type - re-throw the error and Sentry will catch it | ||||
| @@ -303,18 +308,25 @@ class InvenTreeAPI { | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   Future<bool> connectToServer(BuildContext context) async { | ||||
|     print("InvenTreeAPI().connectToServer()"); | ||||
|   bool disconnectFromServer() { | ||||
|     print("InvenTreeAPI().disconnectFromServer()"); | ||||
|  | ||||
|     // Clear connection flag | ||||
|     _connected = false; | ||||
|  | ||||
|     // Clear token | ||||
|     _connecting = false; | ||||
|     _token = ''; | ||||
|     profile = null; | ||||
|   } | ||||
|  | ||||
|   Future<bool> connectToServer(BuildContext context) async { | ||||
|  | ||||
|     // Ensure server is first disconnected | ||||
|     disconnectFromServer(); | ||||
|  | ||||
|     // Load selected profile | ||||
|     profile = await UserProfileDBManager().getSelectedProfile(); | ||||
|  | ||||
|     print("API Profile: ${profile.toString()}"); | ||||
|  | ||||
|     if (profile == null) { | ||||
|       await showErrorDialog( | ||||
|           context, | ||||
| @@ -326,13 +338,13 @@ class InvenTreeAPI { | ||||
|  | ||||
|     _connecting = true; | ||||
|  | ||||
|     _connect(context).then((result) { | ||||
|     bool result = await _connect(context); | ||||
|  | ||||
|     print("_connect() returned result: ${result}"); | ||||
|  | ||||
|     _connecting = false; | ||||
|  | ||||
|     return result; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   // Perform a PATCH request | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| import 'package:InvenTree/widget/dialogs.dart'; | ||||
| import 'package:InvenTree/widget/fields.dart'; | ||||
| import 'package:InvenTree/widget/spinner.dart'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | ||||
| @@ -166,21 +168,20 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> { | ||||
|  | ||||
|   void _selectProfile(BuildContext context, UserProfile profile) async { | ||||
|  | ||||
|     // Mark currently selected profile as unselected | ||||
|     final selected = await UserProfileDBManager().getSelectedProfile(); | ||||
|     // Disconnect InvenTree | ||||
|     InvenTreeAPI().disconnectFromServer(); | ||||
|  | ||||
|     selected.selected = false; | ||||
|  | ||||
|     await UserProfileDBManager().updateProfile(selected); | ||||
|  | ||||
|     profile.selected = true; | ||||
|  | ||||
|     await UserProfileDBManager().updateProfile(profile); | ||||
|     await UserProfileDBManager().selectProfile(profile.key); | ||||
|  | ||||
|     _reload(); | ||||
|  | ||||
|     print("CONNECT FROM A"); | ||||
|     // Attempt server login (this will load the newly selected profile | ||||
|     InvenTreeAPI().connectToServer(context); | ||||
|     InvenTreeAPI().connectToServer(context).then((result) { | ||||
|       _reload(); | ||||
|     }); | ||||
|  | ||||
|     _reload(); | ||||
|   } | ||||
|  | ||||
|   void _deleteProfile(UserProfile profile) async { | ||||
| @@ -191,6 +192,10 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> { | ||||
|     Navigator.of(context).pop(); | ||||
|  | ||||
|     _reload(); | ||||
|  | ||||
|     if (InvenTreeAPI().isConnected() && profile.key == InvenTreeAPI().profile.key) { | ||||
|       InvenTreeAPI().disconnectFromServer(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void _updateProfile(UserProfile profile) async { | ||||
| @@ -201,6 +206,15 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> { | ||||
|     Navigator.of(context).pop(); | ||||
|  | ||||
|     _reload(); | ||||
|  | ||||
|     if (InvenTreeAPI().isConnected() && profile.key == InvenTreeAPI().profile.key) { | ||||
|       // Attempt server login (this will load the newly selected profile | ||||
|  | ||||
|       print("Connect froM A"); | ||||
|       InvenTreeAPI().connectToServer(context).then((result) { | ||||
|         _reload(); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void _addProfile(UserProfile profile) async { | ||||
| @@ -213,11 +227,45 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> { | ||||
|     _reload(); | ||||
|   } | ||||
|  | ||||
|   Widget _getProfileIcon(UserProfile profile) { | ||||
|  | ||||
|     // Not selected? No icon for you! | ||||
|     if (profile == null || !profile.selected) return null; | ||||
|  | ||||
|     // Selected, but (for some reason) not the same as the API... | ||||
|     if (InvenTreeAPI().profile == null || InvenTreeAPI().profile.key != profile.key) { | ||||
|       return FaIcon( | ||||
|         FontAwesomeIcons.questionCircle, | ||||
|         color: Color.fromRGBO(250, 150, 50, 1) | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     // Reflect the connection status of the server | ||||
|     if (InvenTreeAPI().isConnected()) { | ||||
|       return FaIcon( | ||||
|         FontAwesomeIcons.checkCircle, | ||||
|         color: Color.fromRGBO(50, 250, 50, 1) | ||||
|       ); | ||||
|     } else if (InvenTreeAPI().isConnecting()) { | ||||
|       return Spinner( | ||||
|         icon: FontAwesomeIcons.spinner, | ||||
|         color: Color.fromRGBO(50, 50, 250, 1), | ||||
|       ); | ||||
|     } else { | ||||
|       return FaIcon( | ||||
|         FontAwesomeIcons.timesCircle, | ||||
|         color: Color.fromRGBO(250, 50, 50, 1), | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|  | ||||
|     final Size screenSize = MediaQuery.of(context).size; | ||||
|  | ||||
|     print("Building!"); | ||||
|  | ||||
|     List<Widget> children = []; | ||||
|  | ||||
|     if (profiles != null && profiles.length > 0) { | ||||
| @@ -228,10 +276,12 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> { | ||||
|           title: Text( | ||||
|             profile.name, | ||||
|           ), | ||||
|           subtitle: Text(profile.server), | ||||
|           trailing: profile.selected | ||||
|               ? FaIcon(FontAwesomeIcons.checkCircle) | ||||
|               : null, | ||||
|           tileColor: profile.selected ? Color.fromRGBO(0, 0, 0, 0.05) : null, | ||||
|           subtitle: Text("${profile.server}"), | ||||
|           trailing: _getProfileIcon(profile), | ||||
|           onTap: () { | ||||
|             _selectProfile(context, profile); | ||||
|           }, | ||||
|           onLongPress: () { | ||||
|             showDialog( | ||||
|                 context: context, | ||||
| @@ -239,50 +289,40 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> { | ||||
|                   return SimpleDialog( | ||||
|                     title: Text(profile.name), | ||||
|                     children: <Widget>[ | ||||
|                       Divider(), | ||||
|                       SimpleDialogOption( | ||||
|                         onPressed: () { | ||||
|                           Navigator.of(context).pop(); | ||||
|                           _selectProfile(context, profile); | ||||
|                         }, | ||||
|                         child: Text(I18N | ||||
|                             .of(context) | ||||
|                             .profileSelect), | ||||
|                         child: Text(I18N.of(context).profileSelect), | ||||
|                       ), | ||||
|                       SimpleDialogOption( | ||||
|                         onPressed: () { | ||||
|                           Navigator.of(context).pop(); | ||||
|                           _editProfile(context, userProfile: profile); | ||||
|                         }, | ||||
|                         child: Text(I18N | ||||
|                             .of(context) | ||||
|                             .profileEdit), | ||||
|                         child: Text(I18N.of(context).profileEdit), | ||||
|                       ), | ||||
|                       SimpleDialogOption( | ||||
|                         onPressed: () { | ||||
|                           // Navigator.of(context, rootNavigator: true).pop(); | ||||
|                           confirmationDialog( | ||||
|                               context, | ||||
|                               I18N | ||||
|                                   .of(context) | ||||
|                                   .delete, | ||||
|                               I18N.of(context).delete, | ||||
|                               "Delete this profile?", | ||||
|                               onAccept: () { | ||||
|                                 _deleteProfile(profile); | ||||
|                               } | ||||
|                           ); | ||||
|                         }, | ||||
|                         child: Text(I18N | ||||
|                             .of(context) | ||||
|                             .profileDelete), | ||||
|                         child: Text(I18N.of(context).profileDelete), | ||||
|                       ) | ||||
|                     ], | ||||
|                   ); | ||||
|                 } | ||||
|             ); | ||||
|           }, | ||||
|           onTap: () { | ||||
|  | ||||
|           }, | ||||
|         )); | ||||
|       } | ||||
|     } else { | ||||
|   | ||||
| @@ -36,13 +36,13 @@ class UserProfile { | ||||
|   // User ID (will be provided by the server on log-in) | ||||
|   int user_id; | ||||
|  | ||||
|   factory UserProfile.fromJson(int key, Map<String, dynamic> json) => UserProfile( | ||||
|   factory UserProfile.fromJson(int key, Map<String, dynamic> json, bool isSelected) => UserProfile( | ||||
|     key: key, | ||||
|     name: json['name'], | ||||
|     server: json['server'], | ||||
|     username: json['username'], | ||||
|     password: json['password'], | ||||
|     selected: json['selected'] ?? false, | ||||
|     selected: isSelected, | ||||
|   ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
| @@ -50,33 +50,40 @@ class UserProfile { | ||||
|     "server": server, | ||||
|     "username": username, | ||||
|     "password": password, | ||||
|     "selected": selected, | ||||
|   }; | ||||
|  | ||||
|   @override | ||||
|   String toString() { | ||||
|     return "${server} - ${username}:${password}"; | ||||
|     return "<${key}> ${name} : ${server} - ${username}:${password}"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class UserProfileDBManager { | ||||
|  | ||||
|   static const String folder_name = "profiles"; | ||||
|  | ||||
|   final _folder = intMapStoreFactory.store(folder_name); | ||||
|   final store = StoreRef("profiles"); | ||||
|  | ||||
|   Future<Database> get _db async => await InvenTreePreferencesDB.instance.database; | ||||
|  | ||||
|   Future<bool> profileNameExists(String name) async { | ||||
|  | ||||
|     final finder = Finder(filter: Filter.equals("name", name)); | ||||
|  | ||||
|     final profiles = await store.find(await _db, finder: finder); | ||||
|  | ||||
|     return profiles.length > 0; | ||||
|   } | ||||
|  | ||||
|   Future addProfile(UserProfile profile) async { | ||||
|  | ||||
|     UserProfile existingProfile = await getProfile(profile.name); | ||||
|     // Check if a profile already exists with the name | ||||
|     final bool exists = await profileNameExists(profile.name); | ||||
|  | ||||
|     if (existingProfile != null) { | ||||
|     if (exists) { | ||||
|       print("UserProfile '${profile.name}' already exists"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     int key = await _folder.add(await _db, profile.toJson()); | ||||
|     int key = await store.add(await _db, profile.toJson()); | ||||
|  | ||||
|     print("Added user profile <${key}> - '${profile.name}'"); | ||||
|  | ||||
| @@ -84,23 +91,34 @@ class UserProfileDBManager { | ||||
|     profile.key = key; | ||||
|   } | ||||
|  | ||||
|   Future selectProfile(int key) async { | ||||
|     /* | ||||
|      * Mark the particular profile as selected | ||||
|      */ | ||||
|  | ||||
|     final result = await store.record("selected").put(await _db, key); | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
|    | ||||
|   Future updateProfile(UserProfile profile) async { | ||||
|      | ||||
|     if (profile.key == null) { | ||||
|       addProfile(profile); | ||||
|       await addProfile(profile); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     final finder = Finder(filter: Filter.byKey(profile.key)); | ||||
|     await _folder.update(await _db, profile.toJson(), finder: finder); | ||||
|     final result = await store.record(profile.key).update(await _db, profile.toJson()); | ||||
|  | ||||
|     print("Updated user profile <${profile.key}> - '${profile.name}"); | ||||
|     print("Updated user profile <${profile.key}> - '${profile.name}'"); | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   Future deleteProfile(UserProfile profile) async { | ||||
|     final finder = Finder(filter: Filter.equals("name", profile.name)); | ||||
|     await _folder.delete(await _db, finder: finder); | ||||
|  | ||||
|     await store.record(profile.key).delete(await _db); | ||||
|     print("Deleted user profile <${profile.key}> - '${profile.name}'"); | ||||
|   } | ||||
|  | ||||
| @@ -108,78 +126,50 @@ class UserProfileDBManager { | ||||
|     /* | ||||
|      * Return the currently selected profile. | ||||
|      * | ||||
|      * If multiple profiles are selected, | ||||
|      * mark all but the first as unselected | ||||
|      * | ||||
|      * If no profile is currently selected, | ||||
|      * then force the first profile to be selected. | ||||
|      * key should match the "selected" property | ||||
|      */ | ||||
|  | ||||
|     final selected_finder = Finder(filter: Filter.equals("selected", true)); | ||||
|     final selected = await store.record("selected").get(await _db); | ||||
|  | ||||
|     final selected_profiles = await _folder.find(await _db, finder: selected_finder); | ||||
|     final profiles = await store.find(await _db); | ||||
|  | ||||
|     if (selected_profiles.length == 1) { | ||||
|       // A single profile is selected | ||||
|       return UserProfile.fromJson(selected_profiles[0].key, selected_profiles[0].value); | ||||
|     } else if (selected_profiles.length > 1) { | ||||
|       // Multiple selected profiles - de-select others | ||||
|       for (int idx = 1; idx < selected_profiles.length; idx++) { | ||||
|         UserProfile profile = UserProfile.fromJson(selected_profiles[idx].key, selected_profiles[idx].value); | ||||
|     List<UserProfile> profileList = new List<UserProfile>(); | ||||
|  | ||||
|         profile.selected = false; | ||||
|         updateProfile(profile); | ||||
|     for (int idx = 0; idx < profiles.length; idx++) { | ||||
|  | ||||
|       if (profiles[idx].key is int && profiles[idx].key == selected) { | ||||
|         return UserProfile.fromJson( | ||||
|           profiles[idx].key, | ||||
|           profiles[idx].value, | ||||
|           profiles[idx].key == selected, | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|       // And return the first profile | ||||
|       return UserProfile.fromJson(selected_profiles[0].key, selected_profiles[0].value); | ||||
|     } else { | ||||
|       // No profiles selected! | ||||
|  | ||||
|       final all_profiles = await getAllProfiles(); | ||||
|  | ||||
|       if (all_profiles.length == 0) { | ||||
|         // No profiles available | ||||
|     return null; | ||||
|       } else { | ||||
|         UserProfile prf = all_profiles[0]; | ||||
|         prf.selected = true; | ||||
|         updateProfile(prf); | ||||
|  | ||||
|         // Return the selected profile | ||||
|         return prf; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<UserProfile> getProfile(String name) async { | ||||
|  | ||||
|     print("Looking for user profile '${name}'"); | ||||
|  | ||||
|     // Lookup profile by name (or return null if does not exist) | ||||
|     final finder = Finder(filter: Filter.equals("name", name)); | ||||
|  | ||||
|     final profiles = await _folder.find(await _db, finder: finder); | ||||
|  | ||||
|     if (profiles.length == 0) { | ||||
|       print("No matching profiles found"); | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     // Return the first matching profile object | ||||
|     return UserProfile.fromJson(profiles[0].key, profiles[0].value); | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Return all user profile objects | ||||
|    */ | ||||
|   Future<List<UserProfile>> getAllProfiles() async { | ||||
|     final profiles = await _folder.find(await _db); | ||||
|  | ||||
|     final selected = await store.record("selected").get(await _db); | ||||
|  | ||||
|     final profiles = await store.find(await _db); | ||||
|  | ||||
|     List<UserProfile> profileList = new List<UserProfile>(); | ||||
|  | ||||
|     for (int idx = 0; idx < profiles.length; idx++) { | ||||
|       profileList.add(UserProfile.fromJson(profiles[idx].key, profiles[idx].value)); | ||||
|  | ||||
|       if (profiles[idx].key is int) { | ||||
|         profileList.add( | ||||
|             UserProfile.fromJson( | ||||
|               profiles[idx].key, | ||||
|               profiles[idx].value, | ||||
|               profiles[idx].key == selected, | ||||
|             )); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return profileList; | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import 'package:InvenTree/widget/category_display.dart'; | ||||
| import 'package:InvenTree/widget/company_list.dart'; | ||||
| import 'package:InvenTree/widget/location_display.dart'; | ||||
| import 'package:InvenTree/widget/search.dart'; | ||||
| import 'package:InvenTree/widget/spinner.dart'; | ||||
| import 'package:InvenTree/widget/drawer.dart'; | ||||
|  | ||||
| class InvenTreeHomePage extends StatefulWidget { | ||||
| @@ -28,19 +29,10 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> { | ||||
|  | ||||
|   _InvenTreeHomePageState() : super() { | ||||
|  | ||||
|     // Initially load the profile and attempt server connection | ||||
|     _loadProfile(); | ||||
|   } | ||||
|  | ||||
|   String _serverStatus = "Connecting to server"; | ||||
|  | ||||
|   String _serverMessage = ""; | ||||
|  | ||||
|   bool _serverConnection = false; | ||||
|  | ||||
|   FaIcon _serverIcon = new FaIcon(FontAwesomeIcons.spinner); | ||||
|  | ||||
|   Color _serverStatusColor = Color.fromARGB(255, 50, 50, 250); | ||||
|  | ||||
|   // Selected user profile | ||||
|   UserProfile _profile; | ||||
|  | ||||
| @@ -111,30 +103,20 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> { | ||||
|  | ||||
|   void _loadProfile() async { | ||||
|  | ||||
|     final profile = await UserProfileDBManager().getSelectedProfile(); | ||||
|     _profile = await UserProfileDBManager().getSelectedProfile(); | ||||
|  | ||||
|     // If a different profile is selected, re-connect | ||||
|     if (_profile == null || (_profile.key != profile.key)) { | ||||
|  | ||||
|       if (_context != null) { | ||||
|         print("Connecting Profile: ${profile.name} - ${profile.server}"); | ||||
|     // A valid profile was loaded! | ||||
|     if (_profile != null) { | ||||
|       if (!InvenTreeAPI().isConnected() && !InvenTreeAPI().isConnecting()) { | ||||
|  | ||||
|         print("Connect from C"); | ||||
|         InvenTreeAPI().connectToServer(_context).then((result) { | ||||
|           setState(() { | ||||
|  | ||||
|           }); | ||||
|         }); | ||||
|  | ||||
|         setState(() { | ||||
|           setState(() {}); | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     _profile = profile; | ||||
|  | ||||
|     setState(() { | ||||
|  | ||||
|     }); | ||||
|     setState(() {}); | ||||
|   } | ||||
|  | ||||
|   ListTile _serverTile() { | ||||
| @@ -162,10 +144,13 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> { | ||||
|         title: Text("Connecting to server..."), | ||||
|         subtitle: Text("${InvenTreeAPI().baseUrl}"), | ||||
|         leading: FaIcon(FontAwesomeIcons.server), | ||||
|         trailing: FaIcon( | ||||
|           FontAwesomeIcons.spinner, | ||||
|         trailing: Spinner( | ||||
|           icon: FontAwesomeIcons.spinner, | ||||
|           color: Color.fromRGBO(50, 50, 250, 1), | ||||
|         ) | ||||
|         ), | ||||
|         onTap: () { | ||||
|           _selectProfile(); | ||||
|         } | ||||
|       ); | ||||
|     } else if (InvenTreeAPI().isConnected()) { | ||||
|       return ListTile( | ||||
| @@ -196,42 +181,11 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void onConnectSuccess(String msg) async { | ||||
|  | ||||
|     final profile = await UserProfileDBManager().getSelectedProfile(); | ||||
|  | ||||
|     String address = profile?.server ?? 'unknown server address'; | ||||
|  | ||||
|     _serverConnection = true; | ||||
|     _serverMessage = msg; | ||||
|     _serverStatus = "Connected to ${address}"; | ||||
|     _serverStatusColor = Color.fromARGB(255, 50, 250, 50); | ||||
|     _serverIcon = new FaIcon(FontAwesomeIcons.checkCircle, color: _serverStatusColor); | ||||
|  | ||||
|     setState(() {}); | ||||
|   } | ||||
|  | ||||
|   void onConnectFailure(String msg) async { | ||||
|  | ||||
|     final profile = await UserProfileDBManager().getSelectedProfile(); | ||||
|  | ||||
|     _serverConnection = false; | ||||
|     _serverMessage = msg; | ||||
|     _serverStatus = "Could not connect to ${profile?.server}"; | ||||
|     _serverStatusColor = Color.fromARGB(255, 250, 50, 50); | ||||
|     _serverIcon = new FaIcon(FontAwesomeIcons.timesCircle, color: _serverStatusColor); | ||||
|  | ||||
|     setState(() {}); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|  | ||||
|     _context = context; | ||||
|  | ||||
|     _loadProfile(); | ||||
|  | ||||
|     // This method is rerun every time setState is called, for instance as done | ||||
|     // by the _incrementCounter method above. | ||||
|     // | ||||
|   | ||||
							
								
								
									
										54
									
								
								lib/widget/spinner.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								lib/widget/spinner.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/cupertino.dart'; | ||||
|  | ||||
| import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | ||||
|  | ||||
| class Spinner extends StatefulWidget { | ||||
|   final IconData icon; | ||||
|   final Duration duration; | ||||
|   final Color color; | ||||
|  | ||||
|   const Spinner({ | ||||
|     this.color = const Color.fromRGBO(150, 150, 150, 1), | ||||
|     Key key, | ||||
|     @required this.icon, | ||||
|     this.duration = const Duration(milliseconds: 1800), | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   _SpinnerState createState() => _SpinnerState(); | ||||
| } | ||||
|  | ||||
| class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin { | ||||
|   AnimationController _controller; | ||||
|   Widget _child; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     _controller = AnimationController( | ||||
|       vsync: this, | ||||
|       duration: Duration(milliseconds: 2000), | ||||
|     ) | ||||
|       ..repeat(); | ||||
|     _child = FaIcon( | ||||
|       widget.icon, | ||||
|       color: widget.color | ||||
|     ); | ||||
|  | ||||
|     super.initState(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _controller.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return RotationTransition( | ||||
|       turns: _controller, | ||||
|       child: _child, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user