How can I handle a simple In App Subscription with Flutter? - firebase

I'm using the in_app_purchase package and I want to handle a subscription in Flutter.
There is only 1 subscription option and it follows a very common pattern...
I have a section that checks if a Users firebase account 'premium' field is true. If so, it displays the section, if false it displays a 'Subscribe Now' Button.
Currently, on Android, the button doesn't show. On iOS, the button shows but when I click Subscribe - it outputs the PurchaseParams correctly but I'm never prompted for payment. I am testing on a physical device with a sandbox account.
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:wt_flutter/components/trips/blurred_category.dart';
import 'package:wt_flutter/services/globals.dart';
import 'package:wt_flutter/services/models.dart';
import 'package:wt_flutter/shared/loader.dart';
import 'package:wt_flutter/components/trips/initial_airport.dart';
final String proID = 'go_pro_annual';
class TripsScreen extends StatefulWidget {
const TripsScreen({Key key}) : super(key: key);
#override
_TripsScreenState createState() => _TripsScreenState();
}
class _TripsScreenState extends State<TripsScreen> {
// IAP Plugin Interface
InAppPurchaseConnection _iap = InAppPurchaseConnection.instance;
// Is the API available on the device
bool _available = true;
// Subscriptions for sale
List<ProductDetails> _products = [];
// Past purchases
List<PurchaseDetails> _purchases = [];
// Updates to purchases
StreamSubscription _streamSubscription;
#override
void initState() {
_initialize();
super.initState();
}
#override
void dispose() {
_streamSubscription.cancel();
super.dispose();
}
void _initialize() async {
// Check availablility of In App Purchases
_available = await _iap.isAvailable();
if (_available) {
await _getProducts();
await _getPastPurchases();
// List<Future futures = [_getProducts(), _getPastPurchases()];
// await Future.wait(futures);
_verifyPurchase();
// Listen to new purchases
_streamSubscription =
_iap.purchaseUpdatedStream.listen((data) => setState(() {
print('NEW PURCHASE');
_purchases.addAll(data);
_verifyPurchase();
}));
}
}
// Get all products available for sale
Future<void> _getProducts() async {
Set<String> ids = Set.from([proID]);
ProductDetailsResponse response = await _iap.queryProductDetails(ids);
setState(() {
_products = response.productDetails;
});
}
// Gets past purchases
Future<void> _getPastPurchases() async {
QueryPurchaseDetailsResponse response = await _iap.queryPastPurchases();
for (PurchaseDetails purchase in response.pastPurchases) {
if (Platform.isIOS) {
_iap.completePurchase(purchase);
}
}
// Or for consumables
// TODO query the database for state of consumable products
setState(() {
_purchases = response.pastPurchases;
});
}
// Returns purchase of specific product ID
PurchaseDetails _hasPurchased(String productID) {
return _purchases.firstWhere((purchase) => purchase.purchaseID == productID,
orElse: () => null);
}
// Your own business logic to setup a consumable
void _verifyPurchase() {
PurchaseDetails purchase = _hasPurchased(proID);
//TODO serverside verification & record subscription in the database
if (purchase != null && purchase.status == PurchaseStatus.purchased) {
print('Purchase verified');
}
}
/// Purchase a product
void _buyProduct(ProductDetails prod) {
print(prod);
try {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: prod);
// For one time purchase
print(purchaseParam.productDetails.id);
print(purchaseParam.productDetails.price);
print(purchaseParam.productDetails.title);
_iap.buyNonConsumable(purchaseParam: purchaseParam);
print('purchase successful');
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
User user = Provider.of<User>(context);
if (user.homeAirport != '') {
return Scaffold(
body: FutureBuilder(
future: Global.tripsRef.getData(),
builder: (BuildContext context, AsyncSnapshot snap) {
if (snap.hasData) {
return Center(
child: Container(
color: Colors.white,
child: ListView(
physics: ClampingScrollPhysics(),
scrollDirection: Axis.vertical,
children: <Widget>[
Text('Wavetrotter Suggestions'),
TripList(),
for (var prod in _products)
_available && !user.premium
? FlatButton(
child: Text('Subscribe'),
onPressed: () => _buyProduct(prod),
color: Colors.green,
)
: SizedBox(height: 0.0),
Text('Trips For You'),
!user.premium ? BlurredCategory() : TripList(),
Text('Biggest Swells'),
!user.premium ? BlurredCategory() : TripList(),
Text('No Wetsuit'),
!user.premium ? BlurredCategory() : TripList(),
],
),
),
);
} else {
return LoadingScreen();
}
},
),
);
} else {
return InitialAirport();
}
}
}

Related

type 'Future<dynamic>' is not a subtype of type 'Widget'. flutter [duplicate]

This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 10 months ago.
The idea
I want to display followers. the page take list of followers user id and then display their username.
Error
when I tried to I get an Error say type 'Future<dynamic>' is not a subtype of type 'Widget'
The issue in this line Text(user["username"]),
Code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Following extends StatefulWidget {
final following ;
const Following({Key? key, required this.following}) : super(key: key);
#override
_FollowingState createState() => _FollowingState();
}
class _FollowingState extends State<Following> {
/*attribute*/
var following =[];
bool islouded = false;
var usersData= [];
#override
void initState() {
super.initState();
setState(() {
following = widget.following;
});
getFollowing();
}
void getFollowing() {
for(var user in following){
setState(() {
print(user);
// print(getUser(user));
usersData.add( getUser(user));
});
}
setState(() {
islouded = true;
});
}
getUser(uid)async{
try {
if (uid != null) {
var userSnap = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();
var userData = userSnap.data()!;
// print(userSnap.data()!["username"].toString());
return userData;
}
}catch(e){
showSnackBar(context, e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: !islouded?
const Center(
child: CircularProgressIndicator(),
):following.isNotEmpty?
Column(
children: [
for(var user in usersData)
Text(user["username"]),
],
):Text("No following yet!"),
);
}
}
Tried
I tried use FutureBuilder but I did not how to use it right because it return nothing. I believe I'm using it wrong.
the code as follow:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Following extends StatefulWidget {
final following ;
const Following({Key? key, required this.following}) : super(key: key);
#override
_FollowingState createState() => _FollowingState();
}
class _FollowingState extends State<Following> {
/*attribute*/
var following =[];
bool islouded = false;
var usersData= [];
#override
void initState() {
super.initState();
setState(() {
following = widget.following;
});
getFollowing();
}
void getFollowing() {
for(var user in following){
setState(() {
print(user);
// print(getUser(user));
usersData.add( getUser(user));
});
}
setState(() {
islouded = true;
});
}
getUser(uid) async{
try {
if (uid != null) {
var userSnap = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();
return userSnap;
// print(userSnap.data()!["username"].toString());
// return userData;
}
}catch(e){
print(e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: !islouded?
const Center(
child: CircularProgressIndicator(),
):following.isNotEmpty?
Column(
children: [
for(var user in usersData)
FutureBuilder(
future: user,
builder: (context, snapshot){
switch(snapshot.connectionState){
case ConnectionState.none:
return Text("No following yet!");
case ConnectionState.active:
return Text("active");
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
print(user);//Instance of 'Future<dynamic>'
print(snapshot);//AsyncSnapshot<Object?>(ConnectionState.done, Instance of '_JsonDocumentSnapshot', null, null)
return Text("username");//i want to display username but getting different error
default:
return Text("No following yet");
}
}
)
// Text(user["username"]),
],
):Text("No following yet!"),
);
}}
Thank you for taking the time reading my question. I hope you have beautiful day like you <3
I feel this may be the culprit:
usersData.add( getUser(user));.
Try this instead: await usersData.add( getUser(user));.
As you call the async method getUser(user) async { ... } it returns a Future, and this Future gets added to the List not the user. This would explain the error complaining about an unexpected Future.

Await message in flutter waiting woocommerce rest api

i use woocommerce rest api with flutter to get product variations.
Woocommerce rest api is too slowly to get this variations.
I need to send a message to the user to wait for the process to finish.
How to put this message in the code?
#override
Future<List<ProductVariation>> getProductVariations(Product product,
{String lang = 'en'}) async {
try {
final List<ProductVariation> list = [];
int page = 1;
while (true) {
String endPoint =
"products/${product.id}/variations?per_page=100&page=$page";
if (kAdvanceConfig["isMultiLanguages"]) {
endPoint += "&lang=$lang";
}
var response = await wcApi.getAsync(endPoint);
if (response is Map && isNotBlank(response["message"])) {
throw Exception(response["message"]);
} else {
if (response is List && response.isEmpty) {
/// No more data.
break;
}
for (var item in response) {
if (item['visible']) {
list.add(ProductVariation.fromJson(item));
}
}
/// Fetch next page.
page++;
}
}
return list;
} catch (e) {
//This error exception is about your Rest API is not config correctly so that not return the correct JSON format, please double check the document from this link https://docs.inspireui.com/fluxstore/woocommerce-setup/
rethrow;
}
}
Any help?
Refer to this small example:
class Waitscreen extends StatefulWidget {
Waitscreen ({Key key}) : super(key: key);
#override
_WaitscreenState createState() => _WaitscreenState();
}
class _WaitscreenState extends State<Waitscreen> {
bool _isLoading = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: RaisedButton(
child: Text(_isLoading ? "Loading..." : "Load"),
onPressed: () async {
setState((){_isLoading = !_isLoading;});
// TODO
await Future.delayed(Duration(seconds: 5)); // await getProductVariations...
// TODO
setState((){_isLoading = !_isLoading;});
}
),
),
);
}
}
Then you can do something like, according to your need!

Flutter - How to use same Bloc to query specific entry in Firestore with calendar?

I'm currently trying to build a Flutter app using BloC and have encountered some problems i hope you guys could help me with! I'm new to BloC in general so please be understanding.
I have created a BloC for adding, deleting, updating and listing data from firestore, which works fine. What i do need some help with is what i need to do in order to make my code query only entries created on the same day as i pick in my calendar widget with the pickedDate variable i got.
I want the ListView to generate tasks to its correct day, so for example if 2020-05-19 has 2 tasks, it should appear two tasks when picking that day in the calendar. I don't really know what i need to change or do on the calendar in order to achieve this since i still think BloC is a bit confusing.
Do i have to make a seperate bloc for the Calendar or is it okey to use the same bloc that handles my tasks?
Maybe it's just something small i forget to do but i feel completely lost at the moment. Currently i fetch all the firestore entries to a ListView just to see if it works, like this:
Widget _buildCalendarListView(BuildContext context) {
return BlocBuilder<TasksBloc, TasksState>(builder: (context, state) {
if (state is TasksLoading) {
return CircularProgressIndicator();
}
if (state is TasksLoaded) {
return Expanded(
child: ListView.builder(
itemCount: state.tasks.length,
itemBuilder: (context, index) {
var task = state.tasks[index];
return ListTile(
title: Text(task.description),
);
},
),
);
} else {
return Text('No tasks');
}
});
}
And here is my Calendar widget:
Widget _buildTableCalendar() {
return TableCalendar(
onDaySelected: (DateTime day, List events) {
pickedDate = day;
},
calendarController: _calendarController,
startingDayOfWeek: StartingDayOfWeek.monday,
calendarStyle: CalendarStyle(
selectedColor: Colors.blue,
todayColor: Colors.blue[200],
),
);
}
and my BloC for the Tasks where i pass datetime right now:
class TasksBloc extends Bloc<TasksEvent, TasksState> {
final TaskRepository _taskRepository;
StreamSubscription _taskSubscription;
TasksBloc({#required TaskRepository taskRepository})
: assert(taskRepository != null),
_taskRepository = taskRepository;
#override
TasksState get initialState => TasksLoading();
#override
Stream<TasksState> mapEventToState(
TasksEvent event,
) async* {
if (event is LoadTasks) {
yield* _mapLoadTasksToState(DateTime.now());
} else if (event is AddTask) {
yield* _mapAddTaskToState(event);
} else if (event is UpdateTask) {
yield* _mapUpdateTaskToState(event);
} else if (event is DeleteTask) {
yield* _mapDeleteTaskToState(event);
} else if (event is TasksUpdated) {
yield* _mapTaskUpdatedToState(event);
}
}
Stream<TasksState> _mapLoadTasksToState(DateTime queryDate) async* {
_taskSubscription?.cancel();
_taskSubscription = _taskRepository.tasks(DateTime.now()).listen((tasks) {
add(
TasksUpdated(tasks),
);
});
}
Stream<TasksState> _mapAddTaskToState(AddTask event) async* {
_taskRepository.addTask(event.task);
}
Stream<TasksState> _mapUpdateTaskToState(UpdateTask event) async* {
_taskRepository.updateTask(event.updatedTask);
}
Stream<TasksState> _mapDeleteTaskToState(DeleteTask event) async* {
_taskRepository.deleteTask(event.task);
}
Stream<TasksState> _mapTaskUpdatedToState(TasksUpdated event) async* {
yield TasksLoaded(event.tasks);
}
}
and here's the repository i created for firestore:
class FirebaseTaskRepository extends TaskRepository {
final taskCollection = Firestore.instance.collection('tasks');
#override
Future<void> addTask(Task task) {
return taskCollection.add(task.toEntity().toDocument());
}
#override
Future<void> deleteTask(Task task) {
return taskCollection.document(task.id).delete();
}
/* #override
Stream<List<Task>> tasks() {
return taskCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => Task.fromEntity(TaskEntity.fromJson(doc.data)))
.toList();
});
} */
#override
Stream<List<Task>> tasks(DateTime queryDate) {
return taskCollection
.where('date', isGreaterThan: queryDate)
.snapshots()
.map((snapshot) {
return snapshot.documents
.map((doc) => Task.fromEntity(TaskEntity.fromJson(doc.data)))
.toList();
});
}
#override
Future<void> updateTask(Task update) {
return taskCollection
.document(update.id)
.updateData(update.toEntity().toDocument());
}
}
And also my Main.dart, where i use MultiBlocProvider which im not sure if i use correctly and there for i can't achieve what i want? :
void main() {
WidgetsFlutterBinding.ensureInitialized();
BlocSupervisor.delegate = MyBlocDelegate();
final UserRepository userRepository = UserRepository();
runApp(
BlocProvider(
create: (context) =>
AuthenticationBloc(userRepository: userRepository)..add(AppStarted()),
child: App(userRepository: userRepository),
),
);
}
class App extends StatelessWidget {
final UserRepository _userRepository;
App({Key key, #required UserRepository userRepository})
: assert(userRepository != null),
_userRepository = userRepository,
super(key: key);
/* #override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is Unauthenticated) {
return LoginScreen(userRepository: _userRepository);
}
if (state is Authenticated) {
return HomePage(name: state.displayName);
}
return SplashScreen();
},
),
);
} */
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<TasksBloc>(
create: (context) {
return TasksBloc(
taskRepository: FirebaseTaskRepository(),
)..add(LoadTasks());
},
)
],
child: MaterialApp(
routes: {
'/': (context) {
return BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is Unauthenticated) {
return LoginScreen(userRepository: _userRepository);
}
if (state is Authenticated) {
return HomePage(name: state.displayName);
}
return SplashScreen();
},
);
},
'/addTask': (context) {
return NewTaskScreen(
onSave: (task, description, date) {
BlocProvider.of<TasksBloc>(context).add(
AddTask(
Task(task, description: description, date: date),
),
);
},
);
},
'/calendarScreen': (context) {
return CalendarScreen();
}
},
),
);
}
}

Simple Flutter sqflite login->write to db->navigate->retrieve from db flow

What is the correct way to handle this, I have done a lot of searching and most samples which use future builders use them to draw lists so maybe I should be avoiding them all together here.
I want to submit a login form, perform the network request and draw a progress bar while the login is happening, and if successful navigate to a home page. If unsuccessful it should just kill the progress bar and redraw the home page. That part seems to be working, unsure if I am using the Navigator correctly.
The login call returns a user and access token object. The Homepage needs to retrieve the access token which was written to the db by the successful login response. From what I can tell the navigation is happening too quickly and the retrieval of the access token appears to happen before the navigation to the home page.
class LoginPage extends StatefulWidget {
LoginPage({Key key, this.title}) : super(key: key);
final String title;
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool _isValidForm = true;
Future<LoginResponse> _user;
void _submitLogin() {
setState(() {
if (_isValidForm) {
_user = login().then((_) => Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage())));
}
});
}
Widget _buildLoginForm(AsyncSnapshot<LoginResponse> snapshot) {
if (snapshot.connectionState != ConnectionState.none && !snapshot.hasData) {
return new Center(child: new CircularProgressIndicator());
} else {
return SafeArea(
child: Center(
child: new ListView(
children: <Widget>[
//..more views
Padding(
padding: EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//..email and password fields
FlatButton(
child: new Text(
'SIGN IN',
),
onPressed: _submitLogin),
]),
)
],
),
),
);
}
}
#override
Widget build(BuildContext context) {
return new FutureBuilder(
future: _user,
builder: (context, AsyncSnapshot<LoginResponse> snapshot) {
return new Scaffold(
backgroundColor: kMyGreen,
body: _buildLoginForm(snapshot),
);
},
);
}
Future<LoginResponse> login() async {
final response = await http.post(...);
if (response.statusCode == 200) {
var loginResponse = LoginResponse.fromJson(json.decode(response.body));
//Write the user details to local db
DBProvider.db.newUser(loginResponse.user);
//Write the tokens to local db
DBProvider.db.newToken(loginResponse.tokens);
return loginResponse;
} else {
throw Exception('Failed to login');
}
}
}
Database methods:
newUser(User newUser) async {
final db = await database;
//get the biggest id in the table
var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM User");
int id = table.first["id"];
//insert to the table using the new id
var raw = await db.rawInsert(
"INSERT Into User (id,first_name,last_name)"
" VALUES (?,?,?)",
[id, newUser.firstName, newUser.lastName]);
return raw;
}
newToken(Tokens newTokens) async {
final db = await database;
//await db.rawDelete("DELETE FROM Token");
//get the biggest id in the table
var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM Token");
int id = table.first["id"];
//insert to the table using the new id
var raw = await db.rawInsert(
"INSERT Into Token (id,access_token,refresh_token)"
" VALUES (?,?,?)",
[id, newTokens.accessToken, newTokens.refreshToken]);
return raw;
}
Future<Tokens> getToken() async {
final db = await database;
var res = await db.query("Token", limit: 1);
return res.isNotEmpty ? Tokens.fromJson(res.first) : null;
}
Home page
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>{
#override
void initState() {
super.initState();
getHomePageStuff();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Center(
child: RaisedButton(
onPressed: () {},
child: Text('Go back!'),
),
),
);
}
}
Future<HomePageStuffResponse> getHomePageStuff() async {
Tokens token = await DBProvider.db.getToken();
//Accessing the token here throws an NPE
var accessToken = token.accessToken;
debugPrint("token = " + accessToken);
final response = await http.get(..);
if (response.statusCode == 200) {
debugPrint("FETCH SUCCESS");
return stuff;
} else {
throw Exception('Failed to fetch home page stuff');
}
}
You can simply wrap Scaffold's body in FutureBuilder like this
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: FutureBuilder<HomePageStuffResponse>(
future: getHomePageStuff(),
builder: (context, snap) {
if(snap.hasError) {
return ErrorWidget('Error occurred while fetching data');
}
if(snap.hasData) {
return Center(
child: RaisedButton(
onPressed: () {},
child: Text('Go back!'),
),
);
}
}
),
);
}
}
Future<HomePageStuffResponse> getHomePageStuff() async {
Tokens token = await DBProvider.db.getToken();
//Accessing the token here throws an NPE
var accessToken = token.accessToken;
debugPrint("token = " + accessToken);
final response = await http.get(..);
if (response.statusCode == 200) {
debugPrint("FETCH SUCCESS");
return stuff;
} else {
throw Exception('Failed to fetch home page stuff');
}
}
Okay I was pretty close. Navigation is fine the way it is, the issue was the writing to the db was not being awaited on so that would happen simultaneously to the navigation (the newUser and newToken calls). As I would navigate to the home screen and try and read the access token the call would fail because it did not exist yet.
This was made a little harder to figure out because the debugger is a little strange in Android Studio for flutter so I just had to log everything to the console to see the issue.
If you read my question thank you for your time :)

Firestore async load and populate listview flutter

Hi i am trying to get data from firestore and populate listview, following is my code, but i am getting exception since my async call is not complete how can i wait for that asyn call to get completed and populate my listview
my code is below:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:test_flutter/pkg/Feed.dart';
class FeedLoader {
final CollectionReference _colReference;
FeedLoader(Firestore _firestore)
: _colReference = _firestore.collection(Feed.getDocumentName()) {}
Future<List<Feed>> load() async {
final List<Feed> feeds = new List<Feed>();
await for (QuerySnapshot qs in _colReference.snapshots) {
for (DocumentSnapshot ds in qs.documents) {
feeds.add(Feed.fromJson(ds.data));
}
return feeds;
}
return feeds;
}
}
This is my Widget
import 'package:flutter/material.dart';
import 'package:test_flutter/pkg/FeedLoader.dart';
import 'package:test_flutter/pkg/Feed.dart';
class FeedWidget extends StatefulWidget {
final FeedLoader feedLoader;
const FeedWidget({Key key, this.feedLoader}) : super(key: key);
createState() => new FeedWidgetState(feedLoader);
}
class FeedWidgetState extends State<FeedWidget> {
final List<Feed> _feeds = new List<Feed>();
final FeedLoader _feedLoader;
final TextStyle fontStyle = const TextStyle(fontSize: 16.0);
FeedWidgetState(this._feedLoader);
#override
Widget build(BuildContext context) {
print(_feedLoader == null);
_feedLoader
.load()
.then((feeds) => () {
print("Got call back now");
_feeds.addAll(feeds);
})
.catchError((e) => handleError(e));
print("Feeds size ${_feeds}");
return _buildListView(context);
}
void handleError(e) {
print("FeedLoaderException ${e}");
}
Widget _buildListView(BuildContext context) {
return new ListView.builder(
padding: const EdgeInsets.all(6.0),
itemBuilder: (context, i) {
if (i.isOdd) return new Divider();
final index = i ~/ 2;
// pagination
// if (index >= contents.length) {
// contents.addAll(generateWordPairs().take(10));
// }
return _buildRowContent(context, _feeds[i]);
},
);
}
Widget _buildRowContent(BuildContext context, Feed content) {
return new ListTile(
title: new Text(
"${content.getTitle()}",
style: fontStyle,
),
);
}
}
You can use StreamBuilder and FutureBuilder to build widgets asynchronously
You could for example do
return new StreamBuilder(
stream: Firestore....snapshot,
builder: (context, snapshot) {
if (snapshot.hasData) {
final feeds = snapshot.data.map(Feed.fromJson);
return new ListView(
....
);
}
},
)

Resources