How to pass data across Stateful widget? - firebase

So my question is i get data from firebase in first widget, then i click and open a bottomsheet through void -> another stateful widget, how can i pass the snapshot data from first widget to the other one?
Below code is not working...
....
Widget build(BuildContext context) {
return Container(
ElevatedButton(
child: Text('Request a tour'),
onPressed: () {
displayBottomSheet(context, widget.snapshot.data()["author"]);
},
),
);
void displayBottomSheet(BuildContext context, String author) { //updated
showModalBottomSheet(
context: context,
builder: (ctx) {
return BottomSheetWidget(author); //updated
});
}
NEW ERROR: Too many positional arguments: 0 expected, but 1 found.
class BottomSheetWidget extends StatefulWidget {
final String author; //updated
BottomSheetWidget({this.author}); //updated
#override
class _BottomSheetWidgetState createState() => _BottomSheetWidgetState();
}
class _BottomSheetWidgetState extends State<BottomSheetWidget> {
Widget build(BuildContext context) {
return Container(
new ElevatedButton(
child: Text('Send'),
onPressed: () {
requestTour(widget.author); //updated
},
),
.....
}
requestTour(String userName) async {
...
}

class BottomSheetWidget extends StatefulWidget {
final String author; //updated
BottomSheetWidget(this.author); //<-- remove {}
#override
class _BottomSheetWidgetState createState() => _BottomSheetWidgetState();
}
class _BottomSheetWidgetState extends State<BottomSheetWidget> {
Widget build(BuildContext context) {
return Container(
new ElevatedButton(
child: Text('Send'),
onPressed: () {
requestTour(widget.author); //updated
},
),
.....
}
requestTour(String userName) async {
...
}

Just remove curly braces for new arrived error:
replace BottomSheetWidget({this.author}); with BottomSheetWidget(this.author);

Related

Parameter is null instead of an id with ModalRoute.of()

I am developing a mobile App with Flutter and Firebase.
I am trying to use pushNamed() and hand over a parameter. (an id)
I don't know how i could solve my problem.
Here is my Code:
#override
void didChangeDependencies() {
if (_isInit) {
print(ModalRoute.of(context).settings.arguments);
final productId = ModalRoute.of(context).settings.arguments;
if (productId != null) {
_editedAngebot = Provider.of<Angebote>(context).findByID(productId);
_initValues = {
'titel': _editedAngebot.titel,
'beschreibung': _editedAngebot.beschreibung,
'semester': _editedAngebot.semester.toString(),
'fach': _editedAngebot.fach,
'abteilung': _editedAngebot.abteilung,
};
}
}
_isInit = false;
super.didChangeDependencies();
}
And the other class, where I set the parameter. My "Angebot" object only has a default constructor.
trailing: isAllowed()
? IconButton(
icon: Icon(Icons.edit),
onPressed: () {
Navigator.of(context).maybePop();
Navigator.of(context)
.pushNamed('/editAngebot', arguments: id);
})
Why is my ID null?
Your Id is null because you are popping a page first then pushing new page .
Use pushReplacementNamed()
Here is a code sample
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstPage(),
routes:{
Secondpage.routeName:(context)=>Secondpage(),
}
);
}
}
class FirstPage extends StatelessWidget {
final String id = '01';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child:ElevatedButton(
child: Text('GoTo secondPage'),
onPressed: (){
Navigator.of(context).pushReplacementNamed(Secondpage.routeName,arguments: id);
},
))
);
}
}
class Secondpage extends StatelessWidget {
static const routeName = 'secondpage';
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context).settings.arguments as String;
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(child:Text('$data')),
ElevatedButton(
child: Text('GoTo FirstPage'),
onPressed: (){
Navigator.of(context).pop();
},
)
],
)
);
}
}
you should probably change the id defined in the class to a dynamic type and it would work.. tested this and it works as fine
class Product with ChangeNotifier {
//changed to dynamic as errors with null and string came topping up
final dynamic id;
final String title;
final String description;
Product(
{required this.id,
required this.title,
required this.description,
});

Flutter : How to make an http stream for StreamBuilder

Hello
I'm trying to make my first social app with Flutter and I'm stuck.
I would like to get my messages (in a conversasion between tow users) from my api.
Not a probleme when I use Future and Future Builder, but I would like the message list to update when a new message is send !
I found we can achieve it with stream, but every time I try to convert my Future In Stream, it still work, but just as if it was a Future (it never upadate on new message).
here I a simplified part of my code :
class Test extends StatelessWidget {
final Conv conv;
final User otherUser;
const Test({Key key, this.conv, this.otherUser}) : super(key: key);
Stream<List<Message>> messageFlow(String convId) {
return Stream.fromFuture(getMessages(convId));
}
Future<List<Message>> getMessages(String convId) async {
var data = await http
.post(MyApiUrl, headers: <String, String>{}, body: <String, String>{
"someParam": "param",
"id": convId,
});
var jsonData = json.decode(data.body);
List<Message> messages = [];
for (var m in jsonData) {
Message message = Message.fromJson(m);
messages.add(message);
}
return messages;
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: messageFlow(conv.id),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading'),
),
);
}
return ListView.builder(
reverse: true,
controller: _messagesListController,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Message message = snapshot.data[index];
var isMe = message.owner == otherUser.id ? false : true;
return _buildMessage(message, isMe);
});
});
}
}
it would be so nice if you could help me !
I'm not able to replicate your sample code, but here how I understood your question.
Let's first define the difference about Future and Streams:
From this SO post
A Future is like the token with a number on it that they give you when
you order takeout; you made the request, but the result is not yet
ready but you have a placeholder. And when the result is ready, you
get a callback (the digital board above the takeout counter shows your
number or they shout it out) - you can now go in and grab your food
(the result) to take out.
A Stream is like that belt carrying little sushi bowls. By sitting
down at that table, you've "subscribed" to the stream. You don't know
when the next sushi boat will arrive - but when the chef (message
source) places it in the stream (belt), then the subscribers will
receive it. The important thing to note is that they arrive
asynchronously (you have no idea when the next boat/message will come)
but they will arrive in sequence (i.e., if the chef puts three types
of sushi on the belt, in some order -- you will see them come by you
in that same order)
Now here is an example of how you can create your own stream from scratch:
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// 1st approach
final StreamController _streamController = StreamController();
addData()async{
for(int i = 1; i<= 10; i++) {
await Future.delayed(Duration(seconds: 1));
_streamController.sink.add(i);
}
}
// 2nd approach
// This approach will prevent some approach of memory leaks
Stream<int> numberStream() async*{
for(int i = 1; i<= 10; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
_streamController.close();
}
#override
void initState() {
// TODO: implement initState
super.initState();
addData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Stream"),
),
body: Center(
child: StreamBuilder(
stream: numberStream().map((number) => "number $number"),
builder: (context, snapshot){
if(snapshot.hasError)
return Text("hey there is some error");
else if (snapshot.connectionState == ConnectionState.waiting)
return CircularProgressIndicator();
return Text("${snapshot.data}", style: Theme.of(context).textTheme.display1,);
},
)
),
);
}
}
You can also check this SO post for some references.
Here, I tweaked the sample in the SO post above to create a mini simple chat server to show how the messages updates.
import 'dart:async';
import 'package:flutter/material.dart';
class Server {
StreamController<String> _controller = new StreamController.broadcast();
void simulateMessage(String message) {
_controller.add(message);
}
Stream get messages => _controller.stream;
}
final server = new Server();
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => new _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<String> _messages = <String>[];
StreamSubscription<String> _subscription;
#override
void initState() {
_subscription = server.messages.listen((message) async => setState(() {
_messages.add(message);
}));
super.initState();
}
#override
void dispose() {
_subscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.display2;
return new Scaffold(
appBar: new AppBar(
title: new Text('Sample App'),
),
body: new ListView(
children: _messages.map((String message) {
return new Card(
child: new Container(
height: 100.0,
child: new Center(
child: new Text(message, style: textStyle),
),
),
);
}).toList(),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
new FloatingActionButton(
child: new Icon(Icons.account_circle_outlined),
onPressed: () {
// simulate a message arriving
server.simulateMessage('Hello World');
},
),
SizedBox(
height: 20.0,
),
new FloatingActionButton(
child: new Icon(Icons.account_circle_rounded),
onPressed: () {
// simulate a message arriving
server.simulateMessage('Hi Flutter');
},
),
],
),
);
}
}
class SampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomeScreen(),
);
}
}
void main() {
runApp(new SampleApp());
}
And here are some tutorials for better references:
https://www.youtube.com/watch?v=nQBpOIHE4eE
https://www.youtube.com/watch?v=OTS-ap9_aXc
this works for me
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as HTTP;
class PeriodicRequester extends StatelessWidget {
Stream<http.Response> getRandomNumberFact() async* {
yield* Stream.periodic(Duration(seconds: 5), (_) {
return http.get("http://numbersapi.com/random/");
}).asyncMap((event) async => await event);
}
#override
Widget build(BuildContext context) {
return StreamBuilder<http.Response>(
stream: getRandomNumberFact(),
builder: (context, snapshot) => snapshot.hasData
? Center(child: Text(snapshot.data.body))
: CircularProgressIndicator(),
);
}
}

How i can pass Data from one widget to another widget in flutter?

I want to pass Data from one widget to another widget.
I'm getting data from firebase and I want the pass that data across the widgets.
How to pass Data from one screen to another screen.
this is how my code looks like.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentindex = 0;
final List<Widget> _children = [
Dashboard(),
Search(),
];
void onTappedBar(int index) {
setState(() {
_currentindex = index;
});
}
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
return StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return return Scaffold(
backgroundColor: Color(0xFFf6f5fb),
body: _children[_currentindex],
bottomNavigationBar: CurvedNavigationBar(
onTap: onTappedBar,
index: _currentindex,
items: <Widget>[
Icon(
Icons.home,
),
Icon(
Icons.search,
],
),
);
});
}
}
From this widget, Data get loaded from firebase. Now I want pass snapshot.data to Dashboard() widget and Search() widget.
i want show the username Dashboard().
this how my ```Dashboard()`` Widget for exmaple
class Dashboard extends StatefulWidget {
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Here i want display the data')
),
);
}
}
Here is the model class:
class User {
final String uid;
User({this.uid});
}
class UserData {
final String uid;
final String username;
final String phonenumber;
UserData({ this.uid, this.username, this.phonenumber });
}
pass it through constructor if it is simple scenario like yours:
class Dashboard extends StatefulWidget {
final UserData userData;
// userData is not optional-named parameter
const Dashboard(this.userData, {Key key}) : super(key: key);
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
#override
Widget build(BuildContext context) {
final UserData userData = widget.userData;//do something with user
return Center(
child: Text('user is ${user.username}')
),
);
}
}
and the HomeState (don't save widgets as fields of the class, create them on the fly):
class _HomeState extends State<Home> {
int _currentindex = 0;
void onTappedBar(int index) {
setState(() {
_currentindex = index;
});
}
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
return StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Scaffold(
backgroundColor: Color(0xFFf6f5fb),
body: Column(
children: [
Dashboard(userData),
Search(userData),
],
),
bottomNavigationBar: CurvedNavigationBar(
onTap: onTappedBar,
index: _currentindex,
items: <Widget>[
Icon(
Icons.home,
),
Icon(
Icons.search,
],
),
);
});
}
}

How to cancel StreamBuilder's assigned stream on navigating to different screen?

Code:
final ref = Firestore.instance.document('some/path');
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Stream _stream;
#override
void initState() {
super.initState();
_stream = ref.snapshots().map((snapshot) {
// <--------------------------------------------------------------- line 1
return snapshot.data;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: _stream,
builder: (_, snapshot) {
if (snapshot.hasData) {
return RaisedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => Page2())),
child: Text("Navigate"),
);
}
return CircularProgressIndicator();
},
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: RaisedButton(
onPressed: () => ref.updateData({'key': "value"}), // <------ line 2
child: Text('Update data'),
),
);
}
}
This code is reproducible, no compile time error. Let me give you an overview.
There are 2 screen Screen1 and Screen2. On first screen, I have set up a StreamBuilder which listens for value provided using _stream. So far so good.
But when I navigate to Screen2 (see line 2), and update the server from it, the Screen1 snapshot gets built again (see line 1). How can I cancel that Stream so that I don't have to be worry about when Screen1 isn't in view?

Flutter Firestore error with BLoC pattern

A newbie in flutter has a lot of stuff that is just starting to figure out now it's BLoC pattern and now I ran into a problem
I can not understand how to fix this error, seems to have written everything correctly
Here generic Interface for all BLoCs
abstract class BlocBase {
void dispose();
}
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider({
Key key,
#required this.child,
#required this.bloc,
}) : super(key: key);
final T bloc;
final Widget child;
#override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>> {
#override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
Here is the second file in which I use BLoC and where it gives an error
Here I use function validateAndCreateData through which I add Tickets
#override
Widget build(BuildContext context) {
final bloc = BlocProvider.of<TicketsBloc>(context);
return Scaffold(
drawer: MyDrawer(),
appBar: AppBar(
title: Text('Sports'),
backgroundColor: Colors.blueGrey[900],
// automaticallyImplyLeading: false,
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
tooltip: 'Share',
onPressed: () {
Navigator.of(context).pushNamed('/second_screen');
}),
IconButton(
icon: Icon(Icons.account_circle),
tooltip: 'Your account',
onPressed: () {
Navigator.of(context)
.pushReplacementNamed('/account_screen');
}),
IconButton(
icon: Icon(Icons.add),
tooltip: 'Add Tickets',
onPressed: () => validateAndCreateData(bloc),
)
]),
body: MyTab(),
);
}
void validateAndCreateData(TicketsBloc bloc) async {
bloc.createData(description, image, name, price);
}
Your error mean you don't have access to the bloc. You must wrap your app with the provider. If not you cannot inherited from this.
return BlocProvider(
child: MaterialApp(
title: 'My App',
home: HomeScreen(),
),
);

Resources