11. REDUX PERFORMANCE
▸ Rebuilding BLoC vs Redux
▸ Importance of memoization
▸ Set distinct on the view model
▸ Keep number of layers to a minimum
11
12. REDUX SAMPLE
▸ Always use Built Value
▸ Avoid using a StoreBuilder in main.dart
▸ Pass view model to the view/avoid mapping
▸ Restructure to group by feature
▸ Only need flutter_redux and redux_logging
12
15. PERSISTENCE
▸ Separate AppState in to data, ui and auth
▸ Persist each section separately
▸ Use PersistUI and PersistData interfaces
▸ Use similar approach to track loading
▸ Clear state if app version is different
15
16. abstract class AppState
implements Built<AppState, AppStateBuilder> {
bool get isLoading;
bool get isSaving;
AuthState get authState;
DataState get dataState;
UIState get uiState;
}
16
17. class StopLoading {}
class PersistUI {}
class PersistData {}
// Client actions
class LoadClientRequest implements StartLoading {}
class LoadClientSuccess implements StopLoading,
PersistData {
17
18. class StopLoading {}
class PersistUI {}
class PersistData {}
// Client actions
class LoadClientRequest implements StartLoading {}
class LoadClientSuccess implements StopLoading,
PersistData {
18
19. class StopLoading {}
class PersistUI {}
class PersistData {}
// Client actions
class LoadClientRequest implements StartLoading {}
class LoadClientSuccess implements StopLoading,
PersistData {
19
20. EntityUIState productReducer(ProductState state, dynamic action) {
return state.rebuild((b) => b
..editing = editingReducer(state.editing, action).toBuilder());
}
EntityUIState productReducer(ProductState state, dynamic action) {
return state.rebuild((b) => b
..editing.replace(editingReducer(state.editing, action)));
} Error: A value of type ‘…’ can't be assigned
to a variable of type ‘…Builder’.
21. EntityUIState productReducer(ProductState state, dynamic action) {
return state.rebuild((b) => b
..editing = editingReducer(state.editing, action).toBuilder());
}
EntityUIState productReducer(ProductState state, dynamic action) {
return state.rebuild((b) => b
..editing.replace(editingReducer(state.editing, action)));
} Error: A value of type ‘…’ can't be assigned
to a variable of type ‘…Builder’.
22. DATA CACHING
▸ All data is cached to the device
▸ Last updated timestamp is stored in the state
▸ Pass the timestamp when requesting new data
▸ Pull to refresh or auto-refresh after 15 minutes
▸ Autoload after certain events. ie, saving an invoice
22
23. int get lastUpdated;
BuiltMap<int, ProductEntity> get map;
bool get isLoaded => lastUpdated > 0;
bool get isStale {
if (! isLoaded) {
return true;
}
return DateTime.now().millisecondsSinceEpoch
23
24. int get lastUpdated;
BuiltMap<int, ProductEntity> get map;
bool get isLoaded => lastUpdated > 0;
bool get isStale {
if (! isLoaded) {
return true;
}
return DateTime.now().millisecondsSinceEpoch
24
25. int get lastUpdated;
BuiltMap<int, ProductEntity> get map;
bool get isLoaded => lastUpdated > 0;
bool get isStale {
if (! isLoaded) {
return true;
}
return DateTime.now().millisecondsSinceEpoch
25
26. VIEW MODELS
▸ View code should be UI/layout focused
▸ Nested view models
▸ Options to pass data to views
▸ Pass AppState as property on view model
▸ Pass field as property on view model
▸ Access AppState in view using context
26
27. MEMOIZATION
▸ Keep build method fast
▸ Simple Dart package available: memoize
▸ memoX where X is the number of parameters
27
30. FORMS
▸ Built Value enables change tracking
▸ Prefer didChangeDependencies() over
iniState()
▸ Avoid @nullable, set default value in
constructor
▸ Static negative counter for new ids
▸ Use completers in view models
30
31. NAVIGATION
▸ Handle navigation in middleware
▸ Pass context in actions
▸ Use completers for wrap up code
▸ Use pop() to pass back value
▸ snackbarCompleter and popCompleter
31
36. CODE GENERATOR
▸ Reduces need for a lot of copy/replace
▸ Two main commands:
▸ Init: Update code with your app info
▸ Make: Generate individual module
▸ Creates a large amount of code
▸ Important to get it right at the beginning
36