How to set initialState from Shared preferences? - redux

I've been trying to add redux to my flutter app
I want to set MaterialUi theme based on user shared preferences
void main() {
final store = Store<AppState>(
reducer,
initialState: AppState.initialState(),
);
runApp(MyApp(store: store));
}
I have this Function which returns what user preferences are
static Future<String> getActiveTheme() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString(_activeTheme) ?? "Light";
}
and my AppState looks like this
class AppState {
String theme;
User user;
AppState(this.theme, this.user);
AppState.initialState()
: theme = 'Light',
user = null;
}
I would want instead of theme: 'Light' to get theme: getActiveTheme()
Thanks in advance

What you want to do is load the theme as part of your main method. You can then pass the theme you've loaded into your AppState constructors. Not that I've added async to your main method, and moved the setting of theme on your AppState to the parameters.
void main() async {
var theme = await getActiveTheme();
final store = Store<AppState>(
reducer,
initialState: AppState.initialState(theme),
);
runApp(MyApp(store: store));
}
class AppState {
// ...
AppState.initialState(this.theme)
: user = null;
}

Related

Flutter programmatically building a referral system with deep link

I need to implement a deep link or referral system with my flutter application. The theory is
Singup and Signin will be handled by custom backend and not firebase
After a user signs up to my application he will be able to refer the app to others and if others install the app the referrer will gain some points.
Most work in this process will be handled by our custom backend. What I need is when someone uses my referral code I want that code during his/her signup.
So this is the service layer I created:
class DynamicLinkService {
final dynamicLink = FirebaseDynamicLinks.instance;
handleDynamicLink() async {
await dynamicLink.getInitialLink();
// dynamicLink.onLink(onSuccess: (PendingDynamicLinkData data) async {
// // something
// },
// onError: (OnLinkErrorException e) async {
// // something
// },
// );
}
Future<String> createDynamicLink() async {
User user = Store.instance.getUser();
String userId = user.id;
print("User id = $userId");
final DynamicLinkParameters dynamicLinkParameters = DynamicLinkParameters(
uriPrefix: 'https://shoppydev.page.link',
link: Uri.parse(
'https://shoppydev.page.link/?invitedBy=$userId',
),
androidParameters: AndroidParameters(
packageName: 'co.company.app',
minimumVersion: 0,
),
iosParameters: IOSParameters(
bundleId: 'co.company.app',
minimumVersion: '0.0.1',
),
socialMetaTagParameters: SocialMetaTagParameters(
title: 'Refer A friend',
description: 'Refer and earn points',
),
);
final ShortDynamicLink shortDynamicLink = await dynamicLink.buildShortLink(
dynamicLinkParameters,
);
final Uri dynamicUrl = shortDynamicLink.shortUrl;
print(dynamicUrl.toString());
return dynamicUrl.toString();
}
void handleSuccessfulLinking(PendingDynamicLinkData? data) async {
final Uri? deepLink = data!.link;
print(deepLink.toString());
if (deepLink != null) {
var isRefer = deepLink.toString().contains('invitedBy');
if (isRefer) {
var code = deepLink.toString().split('invitedBy=')[1];
print(code);
if (code != null) {
// code contains the referrer's user id
// signup with the referrer's id
}
}
}
}
}
As you can see I tried to create a unique referral link with the user id for now. But most guides I am following as well as some github repos did something like this for handling dynamic link:
dynamicLink.onLink(onSuccess: (PendingDynamicLinkData data) async {
// something
},
onError: (OnLinkErrorException e) async {
// something
},
);
Which throws: The expression doesn't evaluate to a function, so it can't be invoked.
Other notes that might help:
Inside my app.dart I have:
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
void initState() {
super.initState();
initDynamicLinks(context);
}
#override
Widget build(BuildContext context) {
final provider = Provider.of<LocaleProvider>(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'App Name',
theme: ThemeData(
primarySwatch: Colors.blue,
),
onGenerateRoute: buildRouter,
locale: provider.locale,
supportedLocales: L10n.all,
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
);
}
/*
Dynamic Links
*/
void initDynamicLinks(BuildContext context) async {
final PendingDynamicLinkData? data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri? link = data?.link;
if (link != null) {
Navigator.pushNamed(context, link.path);
}
}
}
Issues I have faced till now:
I still haven't found a solid documentation on how to get the referral code(which is need for rewarding the referrer).
I have already checked out this two posts on stack:
Implementing referral rewards in Flutter
Flutter - How to pass custom arguments in firebase dynamic links for app invite feature?
In short, I want to create a unique refer link with my user id. Share the user id with someone else and when he/she registers to my app I want to get the referral code attached to the link.
Example: https://app.page.link/?invitedBy=$userId
When someone installs and registers I want the userId so I can pass it to the invitedBy property of SignUpRequest.
Edit: I think I didn't clarify my question enough. So I will set it up with an example:
I want an unique referral link on my phone which I can give to my friend John. And once he downloads and registers the app I want to get some reward points.
So when he sends his SignUpRequest to the Backend I want my referral code to go with that request, so the request will look like:
SignUpRequest()
..name = "John Doe",
..email = "john#gmail.com"
..invitedBy = "...my referral code goes here"
All the other validation and point giving process will be done in the BE
Put all of the below code in the App.dart or Splash screen, basically the first screen
initState
#override
void initState() {
super.initState();
_initDynamicLinks();
}
_initDynamicLinks - this is from where the dynamic link will be launched
Future<void> _initDynamicLinks() async {
final PendingDynamicLinkData data = await instance.getInitialLink();
final Uri deepLink = data?.link;
_handleDynamicLink(deepLink);
FirebaseDynamicLinks.instance.onLink.listen((dynamicLink) {
final uri = dynamicLink.link;
_handleDynamicLink(uri);
}).onError((e) {
print('onLinkError');
print(e.message);
});
}
_handleDynamicLink - this is where you handle the link and parse it
void _handleDynamicLink(Uri deepLink) async {
if (deepLink != null) {
final url = deepLink.toString();
var isRefer = url.contains('invitedBy');
if (isRefer) {
var code = url.split('invitedBy=')[1];
print(code);
if (code != null) {
// code contains the referrer's user id
// signup with the referrer's id
}
}
}
}
I think this way will be more clean
first add this widget
class DynamicLinksWidgetHandler extends StatefulWidget {
const DynamicLinksWidgetHandler({
super.key,
required this.child,
});
final Widget child;
#override
State<DynamicLinksWidgetHandler> createState() =>
_DynamicLinksWidgetHandlerState();
}
class _DynamicLinksWidgetHandlerState extends State<DynamicLinksWidgetHandler> {
#override
void initState() {
super.initState();
_initDynamicLinks();
}
// _initDynamicLinks - this is from where the dynamic link will be launched
Future<void> _initDynamicLinks() async {
final data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri? deepLink = data?.link;
_handleDynamicLink(deepLink);
FirebaseDynamicLinks.instance.onLink.listen((dynamicLink) {
final uri = dynamicLink.link;
_handleDynamicLink(uri);
}).onError((e) {
print('onLinkError');
print(e.message);
});
}
// _handleDynamicLink - this is where you handle the link and parse it
void _handleDynamicLink(Uri? deepLink) async {
log('_handleDynamicLink:$deepLink');
final code = deepLink?.queryParameters['invitedby'];
if (code == null) return;
// save code to backend
log(code);
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
and then wrap it on your app widget like this
runApp(
const DynamicLinksWidgetHandler(
child: MyApp(),
),
);

LateInitialization Error In Flutter main.dart

I am developing my app and I don't know why this late Initialization error has come I use this same code in my other apps as well there I don't face such error but in this main file the error is persisting and I have been trying for so long It doesn't works. bool? userLoggedIn isn't also working flutter doesn't letting it used. Here is my main.dart file code. Also If anyone can tell me how can I handle 2 logins of app shared preferences that would be a lot helpful
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late bool userLoggedIn;
#override
void initState() {
getLoggedInState();
super.initState();
}
getLoggedInState() async {
await HelperFunctions.getUserLoggedInSharedPreference().then((value) {
setState(() {
userLoggedIn = value!;
});
});
}
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dashee',
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: userLoggedIn ? const Dashboard() : Splash());
}
}
LateInitializationError means a variable declared using the late keyword was not initialized on the constructor
You declare this boolean variable:
late bool userLoggedIn
but did not declare a constructor, so it won't be initialized, the obvious thing to do is giving it a value on the constructor, like so:
_MyAppState() {
userLoggedIn = false; // just some value so dart doesn't complain.
}
However may I suggest you don't do that and instead simply remove the late keyword?
Doing that, of course, will give you an error because userLoggedIn is never initialized, but you can fix that by giving it a default value straight on it's declaration or on the constructor initialization:
bool userLoggedIn = false;
or
_MyAppState(): userLoggedIn = false;
note how on the second option I didn't use the constructor's body, you should only declare a variable late if you plan on initializing it on the constructor's body.
This should solve the LateInitializationError that you are getting.
Regarding multiple log-ins
if you want to have three (or more!) log in states, I recommend you declare an enum of those states:
enum LogInState {
LoggedOff,
LoggedInAsRider,
LoggedInAsUser,
}
In order to store said state in sharedPreferences, you could store them as integers:
Future<void> savedLoggedInState(LogInState state) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt('some key', state.index);
}
then to read said value from shared preferences:
Future<LogInState> getLoginState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int index = prefs.getInt('some key') ?? 0;
return LogInState.values[index];
}
finally to display each different log in state, you'd do something like this:
home: _getLogInScreen(),
[...]
Widget _getLogInScreen() {
switch (userLogIn) {
case LogInState.LoggedOff:
return Splash();
case LogInState.LoggedInAsRider:
return RiderDashboard();
case LogInState.LoggedInAsUser:
return UserDashBoard();
}
// if you make a new log in state, you need to add it to the switch
// statement or it will throw an unimplemented error
throw UnimplementedError();
}

Firestore Angular2 Retrieve documents based on current user

I have started developing a mobile app using IONIC, ANGULAR against Google Firestore. This app will consume mostly documents based on the current user and most of my queries I will need to pass in this user. However, I am experiencing issues getting documents from firestore using the following code from my service to the page:
user-profile.service.ts
async get(){
// await this.afAuth.user.subscribe(currentUser => {
// if(currentUser){
// this.userId = currentUser.uid;
// console.log("User Current ID: " + this.userId);
console.log("PP:" +this.afAuth.auth.currentUser.uid)
return this.userProfilesCollection.doc<UserProfile>(this.afAuth.auth.currentUser.uid).snapshotChanges().pipe(
map(doc => {
const id = doc.payload.id;
const data = doc.payload.data();
return{id, ...data };
}
)
);
}
landing-page.page.ts
export class LandingPage implements OnInit {
userProfile : UserProfile;
constructor(
private authService : AuthService,
private loadingController : LoadingController,
private userProfileService: UserProfileService,
private router : Router
) {
}
ngOnInit() {
this.loadUserProfile();
}
async loadUserProfile() {
const loading = await this.loadingController.create({
message: 'Loading user profile'
});
await loading.present();
this.userProfileService.get().then(res=>{
console.log(res);
loading.dismiss();
})
// this.userProfileService.get().then(
// res =>
// {
// loading.dismiss();
// res.subscribe(c=>
// {
// this.userProfile = {
// cellphone: c.data.cellphone,
// dateOfBirth: c.data.dateOfBirth,
// email: c.data.email,
// firstname: c.data.firstname,
// gender: c.data.gender,
// id: c.data.id,
// lastname: c.data.lastname,
// telephone: c.data.telephone,
// userId: c.data.userId,
// website: c.data.website
// };
// });
// }
// );
}
}
Does anyone have a simple example how to achieve this, and I need to use the load profile to navigate across the application as the currently logged in user will be able to manage their profile and the list items (documents) linked to them?

Persist State of Redux on Refresh in React.js

I am using redux in react.js When i refresh the application, redux state also get refreshed. How can I persist the redux state, without using the local storage.
I don't want to know about redux-persist/redux-storage package
In Main Class:
import PersistedStore from "./PersistedStore";
ReactDOM.render(
<Provider store={PersistedStore.getDefaultStore().store}><MainClass />
</Provider>,
document.getElementById('root')
);
add the following class to your project
import {
createStore
} from "redux";
import rootreducer from './RootReducer'
const LOCAL_STORAGE_NAME = "localData";
class PersistedStore {
// Singleton property
static DefaultStore = null;
// Accessor to the default instance of this class
static getDefaultStore() {
if (PersistedStore.DefaultStore === null) {
PersistedStore.DefaultStore = new PersistedStore();
}
return PersistedStore.DefaultStore;
}
// Redux store
_store = null;
// When class instance is used, initialize the store
constructor() {
this.initStore()
}
// Initialization of Redux Store
initStore() {
this._store = createStore(rootReducer, PersistedStore.loadState());
this._store.subscribe(() => {
PersistedStore.saveState(this._store.getState());
});
}
// Getter to access the Redux store
get store() {
return this._store;
}
// Loading persisted state from localStorage, no need to access
// this method from the outside
static loadState() {
try {
let serializedState = localStorage.getItem(LOCAL_STORAGE_NAME);
if (serializedState === null) {
return PersistedStore.initialState();
}
return JSON.parse(serializedState);
} catch (err) {
return PersistedStore.initialState();
}
}
// Saving persisted state to localStorage every time something
// changes in the Redux Store (This happens because of the subscribe()
// in the instore-method). No need to access this method from the outside
static saveState(state) {
try {
let serializedState = JSON.stringify(state);
localStorage.setItem(LOCAL_STORAGE_NAME, serializedState);
} catch (err) {}
}
// Return whatever you want your initial state to be
static initialState() {
return {};
}
}
export default PersistedStore;

Flutter redux thunk action parameters

I am trying to use redux_thunk but what I really don't understand from the demo is how to send parameters to that function. I have a file actions.dart where are
have all the actions. From my component I want to dispatch to that action some parameters so that I make a request to API. For example I want to login with username, password without saving them in state
actions.dart
final ThunkAction<AppState> login = (Store<AppState> store) async {
await new Future.delayed(
new Duration(seconds: 3),
() => "Waiting complete",
);
store.dispatch(OtherAction(....));
};
component.dart
class LoginBtn extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, Function>(
converter: (store) => (){
login(store);
},
builder: (context, callback) {
return RaisedButton(
highlightElevation: 20.0,
child: Text('Login', style: TextStyle(color: Colors.white)),
color: Color(0xFF0f7186),
onPressed: () {
callback();
});
});
}
}
Can someone help me with some quick fix or demo. Thanks!
Something like this?
class MyAction {
String gender;
MyAction(
{this.gender});
ThunkAction<AppState> getDate() {
return (Store<AppState> store) async {...};
}
}
I think the easiest way to do it is using a function for creating the action:
ThunkAction<String> waitAndDispatch(int secondsToWait) {
return (Store<String> store) async {
final searchResults = await new Future.delayed(
new Duration(seconds: secondsToWait),
() => "Search Results",
);
store.dispatch(searchResults);
};
}
then, dispatch it like:
store.dispatch(waitAndDispatch(3));
There are two ways to go about doing this. Two of them wrap the action you're about to dispatch in a class like what you did above.
class MyAction {
String gender;
String name;
MyAction(this.gender, this.name);
void getDate<AppState>(Store<AppState> store) {
print("store is $store, gender is $gender, name is $name");
}
}
1. Create another middleware or modify the current one. Call the getDate() from
within the middleware.
Eg.
// this middleware intercepts any MyAction
void myMiddleware<AppState>(
Store<AppState> store,
dynamic action,
NextDispatcher next
) {
if (action is MyAction) {
action.getDate(store);
} else {
next(action);
}
}
Then we dispatch as such:
store.dispatch(new MyAction("m", "Peterson")) ;
2. Instead of modifying or creating another middleware, we make getDate() a
ThunkAction and call the getDate() before dispatching. Eg.
class MyAction {
String gender;
String name;
MyAction(this.gender, this.name);
ThunkAction<AppState> getDate = (Store<AppState> store) {
print("store is $store, gender is $gender, name is $name");
}
}
Then we dispatch it like so:
store.dispatch(new MyAction(...).getDate)
The second approach, which is the approach you used in the above example is how I would go about doing it because i dont have to meddle with the middleware.
For this functionality you should extend CallableThunkAction<State> based on official docs:
An interface that can be implemented by end-users to create a class-based [ThunkAction].
Sample
class SigninAction extends CallableThunkAction<AuthState> {
SigninAction(this.mobile);
final String mobile;
#override
call(Store<AuthState> store) async {
// call to server endpoint
store.dispatch(results);
}
}
then, dispatch it like:
store.dispatch(SigninAction('mobile number'));

Resources