Streambuilder snapshot is not getting out of waiting state while using bloc - flutter

I am trying to reproduce the hacker news app as shown in this video : https://youtu.be/fahC3ky_zW0. However in my StreamBuilder Snapshot connection state is always coming out to be 'waiting'. Hence my app is not able to populate data.
File: hn_bloc.dart
class HackerNewsBloc {
final _articlesSubject = BehaviorSubject<UnmodifiableListView<Article>>();
var _articles = <Article>[];
List<int> _ids = [
23361987,
23366546,
];
HackerNewBlock() {
_updateArticles().then((_) {
_articlesSubject.add(UnmodifiableListView(_articles));
});
}
Stream<UnmodifiableListView<Article>> get articles => _articlesSubject.stream;
Future<Article> _getArticle(int id) async {
final storyUrl = 'https://hacker-news.firebaseio.com/v0/item/$id.json';
final storyRes = await http.get(storyUrl);
return parseArticle(storyRes.body);
}
Future<Null> _updateArticles() async {
final futureArticles = _ids.map((id) => _getArticle(id));
final articles = await Future.wait(futureArticles);
_articles = articles;
return null;
}
}
File main.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:hackernews/src/article.dart';
import 'package:hackernews/src/hn_bloc.dart';
import 'dart:collection';
import 'dart:async';
void main() {
final hnBloc = HackerNewsBloc();
runApp(MyApp(
bloc: hnBloc,
));
}
class MyApp extends StatelessWidget {
final HackerNewsBloc bloc;
MyApp({
Key key,
this.bloc,
}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hacker News',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(
title: 'Hacker News',
bloc: bloc,
),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
final HackerNewsBloc bloc;
MyHomePage({Key key, this.title, this.bloc}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: StreamBuilder<UnmodifiableListView<Article>>(
stream: widget.bloc.articles,
initialData: UnmodifiableListView<Article>([]),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...');
case ConnectionState.done:
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
return ListView(
children: snapshot.data.map(_buildItem).toList(),
);
// You can reach your snapshot.data['url'] in here
}
return null; // unreachable
},
),
);
}
Widget _buildItem(Article article) {
print(article);
return Padding(
key: Key(article.title),
padding: const EdgeInsets.all(16.0),
child: ExpansionTile(
title: Text(
article.title,
style: TextStyle(fontSize: 24),
),
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text("${article.type}"),
IconButton(
icon: Icon(Icons.launch),
onPressed: () async {
if (await canLaunch(article.url)) {
launch(article.url);
}
},
),
],
),
],
),
);
}
}

Related

Instantiating a List with Provider

I am trying to learn how to use ChangeNotifierProvider and have gotten stuck. I've setup the class as so:
void main() => runApp(
ChangeNotifierProvider(create: (context) => ItemList(),
child: MyApp(),
)
);
class ItemData {
final String title;
final int score;
ItemData({required this.title, required this.score});
}
class ItemList extends ChangeNotifier{
final _items = [];
void add(item){
_items.add(item);
notifyListeners();
}
void update(){
notifyListeners();
}
}
final itemList = ItemList();
Now I want to create the list:
I'm trying to add items by calling:
itemList.add(ItemData({elements}))
but this isn't working. How do I create my list so I can put it into a Listview Builder?
Try this one:
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(
const MyApp(),
);
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider(
create: (context) => ItemList(), child: const NewHomePage()),
);
}
}
class NewHomePage extends StatefulWidget {
const NewHomePage({Key? key}) : super(key: key);
#override
_NewHomePageState createState() => _NewHomePageState();
}
class _NewHomePageState extends State<NewHomePage> {
#override
Widget build(BuildContext context) {
return Consumer<ItemList>(builder: (context, providerItem, child) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0XFF2e3438),
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
providerItem.basketItem.isEmpty
? const Text("No item in the list")
: ListView.builder(
itemCount: providerItem.basketItem.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Title: " + providerItem.basketItem[index].title),
);
}),
ElevatedButton(
onPressed: () {
providerItem.addItem(ItemData(
title: DateTime.now().toString(),
score: DateTime.now().month));
print("data added successfully" +
providerItem.basketItem.length.toString());
},
child: const Text("Add Data")),
],
));
});
}
}
item_data.dart
class ItemData {
final String title;
final int score;
ItemData({required this.title, required this.score});
}
item_list.dart
class ItemList extends ChangeNotifier {
List<ItemData> _items = [];
void addItem(ItemData itemData) {
_items.add(itemData);
notifyListeners();
}
List<ItemData> get basketItem {
return _items;
}
}

Flutter Icons Call From Database

I have icon-name in my database called (for example) "favorite", "people, "home", etc. The code :
Icon(Icons.favorite, size: 25)
How can I replace that to string? I have tried flutter_icons, material_design_icons_flutter, etc.
Icon(Icons.icon.name, size: 25) //icon.name is the method to call the string from API database
Hope you get what I was trying to explain.
You can copy paste run full code below
You can use getIconUsingPrefix of package https://pub.dev/packages/icons_helper and FutureBuilder
Demo code simulate retrieve DB dealy
Future<String> iconName() async {
await Future.delayed(Duration(seconds: 5), () {});
return Future.value("favorite");
}
...
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
...
case ConnectionState.done:
if (snapshot.hasError) {
...
} else {
return Icon(getIconUsingPrefix(name: snapshot.data),
size: 25.0);
}
}
})
working demo
full code
import 'package:flutter/material.dart';
import 'package:icons_helper/icons_helper.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo + Icon Helper Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<String> _future;
Future<String> iconName() async {
await Future.delayed(Duration(seconds: 5), () {});
return Future.value("favorite");
}
#override
void initState() {
_future = iconName();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return Icon(getIconUsingPrefix(name: snapshot.data),
size: 25.0);
}
}
}),
],
),
),
);
}
}
you need to create a map with 'iconName' as key, and IconData as value. and after fetching iconName from db use it to get IconData;

Flutter Could not find the correct Provider<> above Widget when trying to create ExpansionPanelList

I am trying to build ExpansionPanelList and im using Provider there. To be honest i cannot fully understand the Provider but I am using it as i could not figure other way to make it work. I have seen provider used similar way in somewhere else so i think im not completely wrong here?
Thanks for any help!
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Product {
final String name;
final String price;
Product({this.name, this.price});
}
class DatabaseService {
final CollectionReference productsCollection =
Firestore.instance.collection('products');
List<Product> productListFromSnapshot(QuerySnapshot snapshot) {
// cycle through each document and create product list
return snapshot.documents.map((doc) {
return Product(
name: doc.data['name'] ?? "",
price: doc.data['price'] ?? 0,
);
}).toList();
}
Stream<List<Product>> get products {
return productsCollection.snapshots().map(productListFromSnapshot);
}
}
class ProductList extends StatefulWidget {
ProductList({Key key}) : super(key: key);
#override
_ProductListState createState() => _ProductListState();
}
class _ProductListState extends State<ProductList> {
#override
Widget build(BuildContext context) {
final products = Provider.of<List<Product>>(context) ?? [];
return Container(
child: ExpansionPanelList.radio(
children: products.map<ExpansionPanel>((Product product) {
return ExpansionPanelRadio(
value: product.name,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(product.name),
);
},
body: ListTile(
title: Text(product.name),
subtitle: Text('some text here as subtitle text'),
trailing: Icon(Icons.delete),
onTap: () {
setState(() {
products.removeWhere((currentItem) => product == currentItem);
});
},
),
);
}).toList(),
),
);
}
}
____________________
class Home extends StatelessWidget {
final AuthService _auth = AuthService();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[50],
appBar: AppBar(
title: Text('test'),
backgroundColor: Colors.blue[400],
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
onPressed: () async {
await _auth.signout();
},
icon: Icon(Icons.person),
label: Text('LogOut'))
],
),
body: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
HomeScreen({Key key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Center(
child: Column(children: <Widget>[
RaisedButton(
color: Colors.blue[600],
child: Text('ProductList'),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (contex) => ProductList()));
}),
]),
);
}
}

How do I add floatingactionbutton in my ListView in Flutter dart

I want to add a floatingactionbutton in my ListPage on the bottom right corner.
I tried adding it but I am getting error or it is becoming a dead code.
An on press will be implemented on that floatingactionbutton to create a user and that will be reflected in the listview page.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(new AdminPage());
class AdminPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Admin Dashboard',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Admin Dashboard'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: ListPage(),
);
}
}
class ListPage extends StatefulWidget {
#override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
Future _data;
Future getPosts() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection("admins").getDocuments();
return qn.documents;
}
#override
Widget build(BuildContext context) {
Future getPosts() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection("admins").getDocuments();
return qn.documents;
}
navigateToDetail(DocumentSnapshot post){
Navigator.push(context, MaterialPageRoute(builder: (context) => DetailPage(post: post,)));
}
#override
void initState(){
super.initState();
_data = getPosts();
}
return Container(
child: FutureBuilder(
future: _data,
builder: (_, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: Text("Loading..."),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index){
return ListTile(
title: Text(snapshot.data[index].data["email"]),
onTap: () => navigateToDetail(snapshot.data[index]),
);
});
}
}),
);
}
}
class DetailPage extends StatefulWidget {
final DocumentSnapshot post;
DetailPage({this.post});
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title : Text(widget.post.data["name"]),
),
body: Container(
child:Card(
child: ListTile(
title:Text(widget.post.data["email"]),
subtitle: Text(widget.post.data["name"]),
),
),
),
);
}
}
Image of the screen can be found below
You can add floatingActionButton argument on Scaffold
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: ListPage(),
floatingActionButton: FloatingActionButton(
onPressed: () =>{},
child: const Icon(Icons.add),
),
);
You can add FAB in listview by wrapping FloatingActionButton inside of Transform.translate:
floatingActionButton:Transform.translate(
offset: const Offset(-10, -70),
child: FloatingActionButton(
onPressed: () =>{},
child: const Icon(Icons.add),
),
),

How to make pagination in case items as horizontal Scrolling not vertical listview builder in flutter?

I have horizontal listview builder so how to make pagination to load more data in case items for horizontal listview !
You can copy paste run full code below
You can use package https://pub.dev/packages/flutter_pagewise
code snippet
return PagewiseListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
pageSize: PAGE_SIZE,
itemBuilder: this._itemBuilder,
pageFuture: (pageIndex) =>
BackendService.getPosts(pageIndex * PAGE_SIZE, PAGE_SIZE));
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_pagewise/flutter_pagewise.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class PagewiseListViewExample extends StatelessWidget {
static const int PAGE_SIZE = 5;
#override
Widget build(BuildContext context) {
return PagewiseListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
pageSize: PAGE_SIZE,
itemBuilder: this._itemBuilder,
pageFuture: (pageIndex) =>
BackendService.getPosts(pageIndex * PAGE_SIZE, PAGE_SIZE));
}
Widget _itemBuilder(context, PostModel entry, _) {
return Container(
width: 200,
child: ListTile(
leading: Icon(
Icons.person,
color: Colors.brown[200],
),
title: Text(entry.title),
//subtitle: Text(entry.body),
),
);
}
}
class BackendService {
static Future<List<PostModel>> getPosts(offset, limit) async {
final responseBody = (await http.get(
'http://jsonplaceholder.typicode.com/posts?_start=$offset&_limit=$limit'))
.body;
// The response body is an array of items
return PostModel.fromJsonList(json.decode(responseBody));
}
static Future<List<ImageModel>> getImages(offset, limit) async {
final responseBody = (await http.get(
'http://jsonplaceholder.typicode.com/photos?_start=$offset&_limit=$limit'))
.body;
// The response body is an array of items.
return ImageModel.fromJsonList(json.decode(responseBody));
}
}
class PostModel {
String title;
String body;
PostModel.fromJson(obj) {
this.title = obj['title'];
this.body = obj['body'];
}
static List<PostModel> fromJsonList(jsonList) {
return jsonList.map<PostModel>((obj) => PostModel.fromJson(obj)).toList();
}
}
class ImageModel {
String title;
String id;
String thumbnailUrl;
ImageModel.fromJson(obj) {
this.title = obj['title'];
this.id = obj['id'].toString();
this.thumbnailUrl = obj['thumbnailUrl'];
}
static List<ImageModel> fromJsonList(jsonList) {
return jsonList.map<ImageModel>((obj) => ImageModel.fromJson(obj)).toList();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(height: 200, child: PagewiseListViewExample()),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}