Create stream builder - flutter

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());
}
},
);
}
}

Related

Bad condition - Cubit

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();
}
},
);
}
}

Implement verification user exist

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

Create List View with Stream Builder

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,
);
}
},
)

Why this code is not showing CircularProgressIndicator?

This is a simple code which display a different Text based in a random number. I want to show the CircularProgressIndicator when the user push 'next' button and the method 'getRandom' delays 5 secs.
CircularProgressIndicator never is shown...why?
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
Future<String> random;
#override
void initState() {
super.initState();
random = getRandom();
}
Future<String> getRandom() async{
print("getRandom");
Future.delayed(const Duration(seconds: 5));
return "the number is"+Random().nextInt(100).toString();
}
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: AppBar(title: Text("Random Widget")),
body: Center(child:
FutureBuilder(
future:random,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(children: [
Text(snapshot.data,textScaleFactor: 4),
getNextButton()
]);
} else if (snapshot.hasError) {
return Text("ERROR");
}
return CircularProgressIndicator();
}
)
)),
);
}
Widget getNextButton(){
return RaisedButton(
child: Text("NEXT"),
color: Colors.red,
onPressed: () {
setState(() {
random=getRandom();
});
}
);
}
}
Thanks in advance!!
There are several mistakes in your code.
You are passing initialData. So the snapshot.hasData will be true in the beginning itself.
You didn't initialise the random for the first time.
You have to await your Future.delay
Change getRandom implementation:
Future<String> getRandom() async{
await Future.delayed(const Duration(seconds: 5));
return "the number is"+ Random().nextInt(100).toString();
}
Implement initState to initialise random future:
#override
void initState() {
super.initState();
random = getRandom();
}
Change future builder:
FutureBuilder(
future:random,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
return Column(children: [
Text(snapshot.data,textScaleFactor: 4),
getNextButton()
]);
} else if (snapshot.hasError) {
return Text("ERROR");
}
return CircularProgressIndicator();
}
)
If your not using your initialData field in you FutureBuilder than delete and your code will work. If you for some reason need that value add the following statement to the end:
if(snapshot.data != 'starting') {
return CircularProgressIndicator();
}
See the docs for an example of how to use it

How to retry on error with Flutter StreamBuilder?

I have a StreamBuilder object to render a list from a FireStore collection:
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('posts').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
default:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return Post(document: document);
}).toList());
}
});
}
I'm trying to make it so that if the snapshot.hasError, the StreamBuilder tries again. How can i do that?
Generally, you should always combine StreamBuilder with a stateful widget. Otherwise the stream would be recreated every time the build method is called.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Stream<QuerySnapshot> postsStream;
#override
void initState() {
super.initState();
postsStream = Firestore.instance.collection('posts').snapshots();
}
void retryLoad() {
setState(() {
postsStream = Firestore.instance.collection('posts').snapshots();
})
}
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: postsStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return RaisedButton(
child: Text('Retry'),
onPressed: retryLoad,
);
}
// ...
},
);
}
}