I would like to check if the user has already filled in the registration form:
Here is my code for the connectionState:
class LandingPage extends StatelessWidget {
// final Geolocator _geolocator = Geolocator()..forceAndroidLocationManager;
#override
Widget build(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return StreamBuilder<User>(
stream: auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User user = snapshot.data;
if (user == null) {
return SignInPage();
} else {
// _geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.best)
MatchEngine.instance.initialise(user.uid);
return Chat();
}
} else {
return Scaffold(
body: MyAppsCircularProgressIndicator(title: "MyApp",),
);
}
},
);
}
}
this code works fine for connectionstate.
I would like to add in the first code:
if (not signed in) {
show sign in page
} else {
if (not registered)
show register page
else
show home page
}
or
StreamBuilder(
stream: auth.authStateChanges()
builder: (_, snapshot) {
// check connectionState too
if (snapshot.hasData) {
StreamBuilder(
stream: database.userData() // this is a stream you create that reads from `userData/$uid` or similar
builder: (_, snapshot) {
if (snapshot.hasData) {
return HomePage()
} else {
return RegisterPage()
}
}
)
} else {
return SignInPage()
}
}
)
I would like to add the last code to the previous one to have my connectionstate + my redirection to RegisterPage.
I tried everything but to no avail ... could someone help me? Thank you
You could use the provider package and then create a seperate file which has the following code. I personally use this and it works well.
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if (user == null) {
return SignIn();
} else {
return Dashboard();
}
}
}
and in your main.dart file where you are building the material app. Put the wrapper (or whatever you name it) widget instead such as the following.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return FutureBuilder(
// Initialize FlutterFire
future: Firebase.initializeApp(),
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
return ErrorPage();
}
// Show Application
if (snapshot.connectionState == ConnectionState.done) {
return StreamProvider<Help4YouUser>.value(
value: AuthService().user,
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Wrapper(),
),
);
}
// Initialization
return LoadingWidget();
},
);
}
}
Any clarification needed please comment
Related
I'm trying to get to the RegistrationSendCode screen. But unfortunately I am getting a bad status error. Here is my provider and builder -
Provider -
class RegistrationSendCode extends StatelessWidget{
#override
Widget build(BuildContext context){
return BlocProvider<RegistrationSendCodeCubit>(
create: (context) => RegistrationSendCodeCubit(),
child: RegistrationSendCodeBuilder(),
);
}
}
Builder -
class RegistrationSendCodeBuilder extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<RegistrationSendCodeCubit, RegistrationSendCodeState>(
builder: (context, state) {
if(state is RegistrationSendCodeNotLoading) {
RegistrationSendCodeWidget();
} else if(state is RegistrationSendCodeLoading) {
return const Scaffold(
body: Center(child: CircularProgressIndicator(),),
);
} else if(state is RegistrationSendCodeLoaded) {
return FullName();
} else if(state is RegistrationSendCodeError) {
return MyError();
}
throw StateError('err');
},
);
}
}
error -
The following StateError was thrown building BlocBuilder<RegistrationSendCodeCubit, RegistrationSendCodeState>(dirty, dependencies: [_InheritedProviderScope<RegistrationSendCodeCubit?>], state: _BlocBuilderBaseState<RegistrationSendCodeCubit, RegistrationSendCodeState>#cd448):
Bad state: err
The relevant error-causing widget was:
BlocBuilder<RegistrationSendCodeCubit, RegistrationSendCodeState> BlocBuilder: return BlocProvider<RegistrationSendCodeCubit>(
It is expected that when I go to this screen, I should immediately get into the RegistrationSendCodeNotLoading state, which is logical
You are throwing the error, But you are not catching it anywhere
Try removing the line.
class RegistrationSendCodeBuilder extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<RegistrationSendCodeCubit, RegistrationSendCodeState>(
builder: (context, state) {
if(state is RegistrationSendCodeNotLoading) {
return RegistrationSendCodeWidget(); 👈 add return
} else if(state is RegistrationSendCodeLoading) {
return const Scaffold(
body: Center(child: CircularProgressIndicator(),),
);
} else if(state is RegistrationSendCodeLoaded) {
return FullName();
} else if(state is RegistrationSendCodeError) {
return MyError();
}
throw StateError('err'); // ❌ remove this line
},
);
}
}
Builder parameter must return a Widget to display for all states. In RegistrationSendCodeNotLoading state, you're not returning the widget.
It common approach to have else statement instead of else if for RegistrationSendCodeError state without exclusively checking it with condition.
Change code as below,
class RegistrationSendCodeBuilder extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<RegistrationSendCodeCubit, RegistrationSendCodeState>(
builder: (context, state) {
if(state is RegistrationSendCodeNotLoading) {
return RegistrationSendCodeWidget();
} else if(state is RegistrationSendCodeLoading) {
return const Scaffold(
body: Center(child: CircularProgressIndicator(),),
);
} else if(state is RegistrationSendCodeLoaded) {
return FullName();
} else {
return MyError();
}
},
);
}
}
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<AppThemeNotifier>(
builder: (BuildContext context, AppThemeNotifier value, Widget child) {
return Provider(
create: "XXXXXXX",
dispose: "XXXXXXX",
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: AppTheme.getThemeFromThemeMode(value.themeMode()),
home: Base()),
);
},
);
}
}
class Base extends StatefulWidget {
#override
_BaseState createState() => _BaseState();
}
class _BaseState extends State<Base> {
#override
Widget build(BuildContext context) {
return FutureBuilder<SessionAuth>(
future: Provider.of<AppThemeNotifier>(context, listen: false).validate,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
SessionAuth session = new SessionAuth();
if (session.userId != null && session.isLoggedIn) {
return FullApp();
} else if (isFirst) {
return OnBoardingScreen();
}
return LoginScreen();
} else {
return Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
},
);
}
}
I was trying to navigate different screens based on session validation is done through the provider package. (Login Flow Management)
Simply, I want to replace the Screens based on session details
Break Down
If userId is not equal to null and isLoggedin is true -> FullApp
else If user isFirst is true -> OnBoardingScreen
else Login Screen
Error
flutter: The method '>=' was called on null.
flutter: Receiver: null
flutter: Tried calling: >=(0.0)
SessionAuth
class SessionAuth {
SessionAuth({this.isLoggedIn, this.userId, this.isFirst});
int userId;
bool isLoggedIn;
bool isFirst;
}
validate
Future<SessionAuth> get validate async {
SharedPreferences prefs = await SharedPreferences.getInstance();
SessionAuth auth = new SessionAuth();
auth.userId = prefs.getInt('userId') ?? null;
auth.isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
auth.isFirst = prefs.getBool("isFirst") ?? null;
return auth;
}
Get this errors:
1.A value of type 'Stream' can't be returned from function 'stream' because it has a return type of 'Stream'. (return_of_invalid_type at blahblah)
2.The argument type 'Stream' can't be assigned to the parameter type 'Stream'. (argument_type_not_assignable at blahblah)
why?
here is code to create stream builder base on this video from flutter team
void main() {
runApp(MyApp());
//listen to subscribe to stream
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Center(
child: Stream(),
),
),
);
}
}
class Stream extends StatefulWidget {
#override
_StreamState createState() => _StreamState();
}
class _StreamState extends State<Stream> {
#override
Widget build(BuildContext context) {
return StreamBuilder(
//Error number 2
stream: NumberCreator().stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.done) {
return Text('done');
} else if (snapshot.hasError) {
return Text('Error!');
} else {
return Text(snapshot.data);
}
},
);
}
}
class NumberCreator {
NumberCreator() {
Timer.periodic(Duration(seconds: 1), (timer) {
//add count to stream
_controller.sink.add(_count);
_count++;
});
}
var _count = 1;
final _controller = StreamController<int>();
//error number 1
Stream<int> get stream => _controller.stream;
dispose() {
_controller.close();
}
}
and Do we need statefulWidget to create stramBuilder?
The name of your widget class is causing problem because
class Stream extends StatefulWidget
is causing conflict with the name used by StreamBuildWidget parameter
stream: NumberCreator().stream,
So Try Changing your widgetName to something like StreamPage etc.
and also change
return Text(snapshot.data);
to
return Text(snapshot.data.toString());
even this is throwing error Text widget expects the String not int
Corrected Version of code
class StreamBuilderPage extends StatefulWidget {
#override
_StreamBuilderPageState createState() => _StreamBuilderPageState();
}
class _StreamBuilderPageState extends State<StreamBuilderPage> {
#override
Widget build(BuildContext context) {
return StreamBuilder(
//Error number 2
stream: NumberCreator().stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.done) {
return Text('done');
} else if (snapshot.hasError) {
return Text('Error!');
} else {
return Text(snapshot.data.toString());
}
},
);
}
}
Want to create a list view from live data from server using stream,
so create fake stream of data and then save its snapshot in a list to test result, when use that list in ListTile and run app get following error:
The following assertion was thrown building ListTile(dirty):
No Material widget found.
ListTile widgets require a Material widget ancestor.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Center(
child: StreamBuilderPage(),
),
),
);
}
}
class StreamBuilderPage extends StatefulWidget {
#override
_StreamBuilderPageState createState() => _StreamBuilderPageState();
}
class _StreamBuilderPageState extends State<StreamBuilderPage> {
List<int> items = [];
#override
Widget build(BuildContext context) {
return StreamBuilder(
//Error number 2
stream: NumberCreator().stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.done) {
return Text('done');
} else if (snapshot.hasError) {
return Text('Error!');
} else {
items.add(snapshot.data);
print(items); //print every second: [0] then [0,1] then [0,1,2] ...
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].toString()),
);
},
itemCount: items.length,
);
}
},
);
}
}
class NumberCreator {
NumberCreator() {
Timer.periodic(Duration(seconds: 1), (timer) {
//add count to stream
_controller.sink.add(_count);
_count++;
});
}
var _count = 1;
final _controller = StreamController<int>();
Stream<int> get stream => _controller.stream;
dispose() {
_controller.close();
}
}
What actually cause this error?
Thanks community.
Try this
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: StreamBuilderPage(),
);
}
}
class StreamBuilderPage extends StatefulWidget {
#override
_StreamBuilderPageState createState() => _StreamBuilderPageState();
}
class _StreamBuilderPageState extends State<StreamBuilderPage> {
List<int> items = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: StreamBuilder(
//Error number 2
stream: NumberCreator().stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.done) {
return Text('done');
} else if (snapshot.hasError) {
return Text('Error!');
} else {
items.add(snapshot.data);
print(
items); //print every second: [0] then [0,1] then [0,1,2] ...
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].toString()),
);
},
itemCount: items.length,
);
}
},
),
),
),
);
}
}
class NumberCreator {
NumberCreator() {
Timer.periodic(Duration(seconds: 1), (timer) {
//add count to stream
_controller.sink.add(_count);
_count++;
});
}
var _count = 1;
final _controller = StreamController<int>();
Stream<int> get stream => _controller.stream;
dispose() {
_controller.close();
}
}
I think you have to embed ListTile in a Material specific widget such as scaffold. I had a similar issue a few days ago and somewhere the message tells you which widgets may be wrapped around to prevent this error
StreamBuilder(
//Error number 2
stream: FunctionWhichReturnSomething().stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.done) {
return Text('done');
} else if (snapshot.hasError) {
return Text('Error!');
} else {
items.add(snapshot.data);
print(
items); //print every second: [0] then [0,1] then [0,1,2] ...
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].toString()),
);
},
itemCount: items.length,
);
}
},
)
I have this app where I have an intro screen that I want to hide if the user has already skipped once.
I'm using bloc with a Provider.
My issue is the connection state changes twice when I hot restart the app and I've been spending hours without understanding the reason.
Here is my code:
my main class
void main() => runApp(StatsApp());
class StatsApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => IntroProvider(),
)
],
child: Consumer<IntroProvider>(builder: (context, value, child) {
return MaterialApp(
home: FutureBuilder(
future: value.bloc.checkSkipped(),
builder: (BuildContext context, snapshot) {
print(snapshot.connectionState);
print(snapshot.data);
return SplashScreen();
},
));
}),
);
}
}
my bloc
enum IntroEvents {
ReadLocalStorage,
SetIntroSkipped,
}
class IntroBloc extends Bloc<IntroEvents, bool> {
PrefsManager _prefsManager = PrefsManager.instance;
Future<bool> checkSkipped() async {
this.add(IntroEvents.ReadLocalStorage);
final skipped =
await _prefsManager.getValue(PrefTypes.Bool, "skippedIntro");
return skipped;
}
#override
// TODO: implement initialState
bool get initialState => false;
#override
Stream<bool> mapEventToState(IntroEvents event) async* {
switch (event) {
case IntroEvents.SetIntroSkipped:
_prefsManager.setValue(PrefTypes.Bool, "skippedIntro", true);
yield true;
break;
case IntroEvents.ReadLocalStorage:
final skipped =
await _prefsManager.getValue(PrefTypes.Bool, "skippedIntro");
yield skipped;
break;
default:
print("wtffffff");
}
}
}
my provider
class IntroProvider with ChangeNotifier {
IntroBloc _bloc;
IntroProvider(){
print("called IntroProvider");
_bloc = IntroBloc();
}
IntroBloc get bloc => _bloc;
}
Any help would be highly appreciated.
When working with snapshots you're able to check whether the snapshot.hasData. Normally you'd wrap the functionality you want to run when the snapshot has data in an if statement and provide some kind of default Widget when it does not.
FutureBuilder(
future: value.bloc.checkSkipped(),
builder: (BuildContext context, snapshot) {
print(snapshot.connectionState);
if (snapshot.hasData) {
print(snapshot.data);
}
return SplashScreen();
},
);