Boolean expression must not be null? - database

Does anyone know why i got the Failed assertion: boolean expression must not be null.
If I had logged in and quit the app and open the app again i should be directly in the homescreen.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool userIsLoggedIn = false;
#override
void initState(){
getLoggedInState();
super.initState();
}
getLoggedInState()async{
await HelperFunction.getUserLoggedInSharedPreference().then((value){
setState(() {
userIsLoggedIn = value;
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: userIsLoggedIn ? HomeScreen() : CompanyLoadingBar(),
);
}
}

If your 'value' here can become null - you get the error.
To be safe change the line inside setState to:
userIsLoggedIn = value ?? false;
it will set the bool to false if value is null

Your problem is that you are not waiting for your Future to resolve to an actual value.
This line:
userIsLoggedIn = value;
might not have been reached when your build method is called. Because it's async and you don't await it.
You can set a default value to userIsLoggedIn, maybe false, that will solve your immediate problem. But it will not solve your real problem, that your program logic is asynchronous.
You will probably want to build your app with at least one FutureBuilder.
See: What is a Future and how do I use it?

Your issue most likely lies in this function where you're setting userIsLoggedIn to null. Ensure getUserLoggedInSharedPreference actually returns a boolean and not null.
void getLoggedInState() async {
final result = await HelperFunction.getUserLoggedInSharedPreference();
if (result != null) {
setState(() {
userIsLoggedIn = result;
});
} else {
print("result is null");
}
}

You have a global approach error. You combine traditional and async/await methods in this code:
getLoggedInState()async{
await HelperFunction.getUserLoggedInSharedPreference().then((value){
setState(() {
userIsLoggedIn = value;
});
});
}
If you use async/await you should not use then method.
To implement what you want you should use something like this:
...
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: FutureBuilder<bool>(
future: HelperFunction.getUserLoggedInSharedPreference(),
builder: (context,snapshot) {
if (snapshot.hasData) {
// Future is ready. Take data from snapshot
userIsLoggedIn = snapshot.data; // bool value is here
return userIsLoggedIn ? HomeScreen() : CompanyLoadingBar();
} else {
// Show progress while future will be completed
return CircularProgressIndicator();
}
}
),
);
}
...
And also check what value is returned from HelperFunction.getUserLoggedInSharedPreference(). It seems it returns null because I think you have no already saved value. So you need to specify the default value:
final value = await SharedPreferences.getInstance().readBool(name) ?? false;

Related

MobX - Reaction inside class component

Today I started using MobX and the first problem I ran into is how to execute a function in a React class component whenever an Observable updates.
I am under the impression this can be achieved using a reaction, but I'm not sure how to make it work.
class MissionLog {
private _missions: Array<IMissionItem> = [];
public get missions() {
return this._missions;
}
constructor() {
makeAutoObservable(this);
}
// Example of a method that modifies the _missions array
public receiveMission(mission: IMissionItem) {
this._missions.push(mission);
}
}
export const missionLog = new MissionLog();
// Example of modifying the missions array
missionLog.receiveMission(someMission);
export const ObserverTest = observer(class _ObserverTest extends React.Component {
constructor(props: any) {
super(props);
// Executes the console.log at the start,
// but not when missionLog.missions changes.
autorun(() => {
console.log("Autorun", missionLog.missions);
})
// Never executes the console.log
reaction(
() => missionLog.missions,
(mission) => {
console.log("Reaction");
}
)
}
render() {
return (
// Accessing missionLog.missions here
// gives me the correct, updated data,
// so my setup should be fine.
)
}
});
I also tried to use intercept and observe instead of reaction, but also no result.

What is the equivalent for `componentDidMount()` in Flutter

I coming from ReactJS and React Native. I want to try out Flutter. So far I want to have a login screen. Therefore I want to check if a user is already logged in. If so forward to the Home Screen. If not, show the login screen.
In React with TypeScript and Firebase I would to it this way:
interface RootScreenState {
isLoading: boolean;
user: firebase.User | null;
}
class RootScreen extends React.Component<{}, RootScreenState> {
constructor(props) {
super(props);
this.state = {
isLoading: true,
user: null
}
}
componentDidMount() {
// make an async call to some firebase libraries to look for stored user credentials
// if some credentials are found try to login
// of no credentials are found or login fails return null
// otherwise the user is returned
tryToLogin().then((currentUser: firebase.User | null) => {
this.setState({
isLoading: false,
user: currentUser
}).catch(err => { /* do some error handling */});
}
render() {
const { isLoading, user } = this.state;
if(isLoading) return ( /* loading screen */ );
else if(user === null) return ( /* login screen */ );
else return ( /* home screen */ );
}
}
How do I do with Flutter? I could not find anything about an equivalent to compnentDidMount(), should I do it in the constructor? In React this would fail.
use initState in Stateful widget.InitState is called when the stateful widget is first time painted. For ex
class _MyAppState extends State<MyApp> {
Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
You can do this in Flutter's initState.
It gets called first when your widget tree is rendered.
Check the code below:
#override
void initState() {
super.initState();
}
For more details on various things in React Native you wished to know in Flutter.
See the link below: It is the official documentation of people coming to Flutter from a React Native background.
Documentation
I hope this helps.
When it comes to request data from the server, you may want to use FutureBuilder directly.
Sometimes widget build will trigger twice if you put states in initial of state improperly.
I prefer to put FutureBuilder into widget build scope and more clean and readable for me.
In your case, as you also work with navigation, I would work with a FutureBuilder. Initialize the framework, look for the user and navigate the user to the proper screen based on the state with initialroute.
If you want it nicer, you can play around with a SplashScreen. A good package you can use is flutter_native_splash. You will also find a full example there.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
// The event you are waiting for (can also be your user object, once loaded)
future: _initializeFrameworks,
builder: (context, snapshot) {
// Show splash screen
if (snapshot.connectionState == ConnectionState.waiting) {
return MaterialApp(home: SplashScreen());
} else {
// Return app and navigate
return MaterialApp(
initialRoute: snapshot.data ? MainScreen.id : WelcomeScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
MainScreen.id: (context) => MainScreen(),
},
);
}
},
);
}
}
class Mainscreen extends StatelessWidget {
static const id = "main_screen";
// your widget goes here
}
class WelcomeScreen extends StatelessWidget {
static const id = "welcome_screen";
// your widget goes here
}

Assinging value to variable outside class

I am accessing JSON file in ComponentDidMount in class A, i need to access that result outside class and need to use that in Class B
let test;
console.log(test);
class CustomerPage extends React.Component {
componentDidMount(): void {
$.getJSON("/api/LocaleStrings")
.done(results => {
let JsonString = JSON.parse(results);
test = new LocalizedStrings(JsonString);
})
.fail(console.log.bind(console));
}
}
Here, console.log(test) yields undefined.
It seems to me that your console.log(test) gets executed before the AJAX call returns, and at that point it will be uninitialized (undefined). Place your console.log inside the done function.
You could store your AJAX result in your component's state:
class CustomerPage extends React.Component {
constructor(props) {
super(props);
this.state = { test: null };
}
componentDidMount(): void {
$.getJSON("/api/LocaleStrings")
.done(results => {
let JsonString = JSON.parse(results);
this.setState({
test: new LocalizedStrings(JsonString);
});
})
.fail(console.log.bind(console));
}
}
You need to have an "event" that notifies anyone who is interested that test is available:
interface CustomerPageProps {
onLocaleStringsLoaded?: (test:object) => void
}
class CustomerPage extends React.Component<CustomerPageProps> {
static defaultProps {
onLocaleStringsLoaded: () => {} //nothing by default
}
componentDidMount(): void {
$.getJSON("/api/LocaleStrings")
.done(results => {
let JsonString = JSON.parse(results);
const test = new LocalizedStrings(JsonString);
this.props.onLocaleStringsLoaded(test);
}).fail(console.log.bind(console));
}
}
Then at some point in your code you could have:
<CustomerPage onLocaleStringsLoaded={window.console.log.bind(window.console)} />
which will print to the console once the result is available.
I recommend reading up a bit more on how React components share data. The component that needs the data can have an input defined, in which you can pass the test variable. Or using a redux store (which could potentially be a little too complex for your application). If you really want to continue this route. You can always use the window object to set a global variable: window.test = 'bla';. This is available anywhere in the application with console.log(window.test);.
You would have to update your code to:
window.test = new LocalizedStrings(JsonString);.
Verifying that it is set can be done with an interval:
setInterval(function() {
console.log(window.test);
}, 100);

TypeScript + React: Avoid setState on unmounted components

I know that a common pattern of avoiding calling .setState() on an unmounted component is by adding a private property such as _isMounted to keep track of it, as mentioned in a blog.
I've been using this method like this:
class Hello extends React.PureComponent{
_isMounted = false;
constructor(props) {
super(props);
this.state = {
// ...
};
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
// ...
}
Everything was fine until I started to use TypeScript. I tried to do this in the same way:
class Hello extends React.PureComponent<Props, State> {
private _isMounted: boolean;
constructor(props: Props) {
super(props);
this.state = {
// ...
};
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
}
But then it always throws a type error:
TypeError: Cannot set property _isMounted of #<Component> which has only a getter
For now, the only solution I know is to explicitly write a setter for it. But I don't really understand if this is the expected way to do it. Doesn't TypeScript generate getter and setter automatically?
Updated:
A codesandbox example: https://codesandbox.io/s/l59wnqy5zz
Somehow it's the name, to me _isMounted actually works but not isMounted.
#Stramski points out, I think _isMounted is a private property of React.Component. Rename it and it should work.
To confirm, I rename the variable to mounted, it worked.
It might be useful for other developers
Using getters and setters
class Hello extends React.Component {
private isFullyMounted: boolean = false;
public componentDidMount() {
this.isMounted = true;
}
public componentWillUnmount() {
this.isMounted = false;
}
public set isMounted(status: boolean) {
this.isFullyMounted = status;
}
public get isMounted() {
return this.isFullyMounted;
}
}

How to access class element variable in React/TypeScript

I have a class level variable that I want to be able to access in my functions. I thought this worked like a global variable but I'm new to this. This is the simplified code I have right now that is not working:
class People extends React.Component<PeopleProps, {}> {
public isAdmin: boolean = false;
public render() {
return <div onClick={this._checkAdmin}>Call function</div>;
}
private _checkAdmin() {
if (this.isAdmin) {
console.log("is admin");
}
}
}
The error occurs on the if statement. The error I am getting is Uncaught
TypeError: Cannot read property 'isAdmin' of null
I am assuming that this is undefined but I'm not sure how that could happen. There are no errors in VS before building this. I can get to the _checkAdmin function so it seems like this is working there. Do I need to pass an argument to '_checkAdmin'?
Missed binding this
class People extends React.Component<PeopleProps, {}> {
public isAdmin: boolean = false;
public render() {
return <div onClick={this._checkAdmin.bind(this)}>Call function</div>;
}
private _checkAdmin() {
if (this.isAdmin) {
console.log("is admin");
}
}
}
As said cooper, you forgot bind this. But you don't care about this if you use arrow functions:
class People extends React.Component<PeopleProps, {}> {
public isAdmin: boolean = false;
public render() {
return <div onClick={this._checkAdmin}>Call function</div>;
}
private _checkAdmin = () => {
if (this.isAdmin) {
console.log("is admin");
}
}
}

Resources