Futurebuilder in StatelessWidget - Flutter - flutter

I have the following code in a Stateful Widget and the future: in Futurebuilder correctly calls the function _getCategory()
class Categories extends StatefulWidget {
#override
_CategoriesState createState() => _CategoriesState();
}
class _CategoriesState extends State<Categories> {
List<AllAccounts> _allaccounts;
DatabaseHelper _dbHelper;
int _count = 0;
#override
void initState() {
super.initState();
setState(() {
_dbHelper = DatabaseHelper.instance;
});
_getCategory();
}
Future _getCategory() async {
List<AllAccounts> x = await _dbHelper.getCategory();
setState(() {
_allaccounts = x;
});
_count = _allaccounts.length;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add/Edit Categories'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
height: 20.0,
),
Text('Select a Category', style: TextStyle(fontSize: 18.0),),
SizedBox(
height: 20.0,
),
_list(),
],
),
),
);
}
_list() =>
Expanded(
child: ListView.builder(
itemCount: _count,
itemBuilder: (context, position) {
return Card(
color: Colors.grey[600],
elevation: 2.0,
child: ListTile(
leading: Icon(Icons.group),
title: Text(_allaccounts[position].category,
style: TextStyle(fontSize: 17.0),),
trailing: Icon(Icons.arrow_forward),
onTap: (){},
),
);
},
),
);
}
However, when I try the same thing in a StatelessWidget, the future: in Futurebuilder is not called.
class Test extends StatelessWidget {
List<AllAccounts> _allaccounts;
DatabaseHelper _dbHelper;
int _count = 0;
Future<int> _getCat() async {
List<AllAccounts> x = await _dbHelper.getCategory();
_allaccounts = x;
_count = x.length;
print(_count);
return _count;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_list(),
],
),
),);
}
_list() => Expanded(
child: FutureBuilder<int>(
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.none &&
snapshot.hasData == null) {
return Center(
child: CircularProgressIndicator(),
);
}
return Container(
child: Text('Printing $_count'),
);
},
future: _getCat(),
),
);
}
Text('Printing $_count) returns 'Printing 0'. Also the print in the _getCat() function does not show-up in the console.
Is there a problem in the way Futurebuilder is used in the Stateless Widget?

Instead of printing _count, you need to use snapshot.data to get the value returned from _getCat.
return Container(
child: Text('Printing ${snapshot.data}'),
);

Related

How to create load more listview in flutter

I want to create load more scrollview in listview. My app flow is storing youtube link in csv file and fetch this link from my app and display in my listview. But the problem is I don't want to wait too much load time when app is open.If I have a lot of youtube link in my csv.I will take a lot of time.So,for example I want to display only 5 video in initial state and when load more, display more 5 video in my list view.How can I do that.My code is below.
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'videolist.dart';
import './models/models.dart';
import 'package:csv/csv.dart' as csv;
import 'package:http/http.dart' as http;
class DisplayVideo extends StatefulWidget {
String id;
#override
DisplayVideo(this.id);
_DisplayVideoState createState() => _DisplayVideoState();
}
class _DisplayVideoState extends State<DisplayVideo> {
late YoutubePlayerController _controller ;
Future<List<YoutubeDetail>> _loadCSV() async {
Map<String, String> allData = {
'login': '',
'password': '',
};
final Uri url = Uri.parse(
'https://raw.githubusercontent.com/JornaldRem/bedtime_story/main/videoId.csv');
final response = await http.get(url);
csv.CsvToListConverter converter =
new csv.CsvToListConverter(eol: '\r\n', fieldDelimiter: ',');
List<List> listCreated = converter.convert(response.body);
// the csv file is converted to a 2-Dimensional list
List<YoutubeDetail> youtubeDetailList = [];
for (int i = 0; i < listCreated.length; i++) {
YoutubeDetail temp = YoutubeDetail(
listCreated[i][0],
listCreated[i][1],
);
youtubeDetailList.add(temp);
}
return youtubeDetailList;
}
#override
void initState() {
// TODO: implement initState
super.initState();
_controller = YoutubePlayerController(
initialVideoId: widget.id,
flags: YoutubePlayerFlags(
autoPlay: true,
mute: false,
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: Text('Title'),
toolbarHeight: 60,
backgroundColor: const Color(0xFF006666),
),
body: Column(
children: [
Container(
child: YoutubePlayer(
controller: _controller,
liveUIColor: Colors.amber,
),
),
Expanded(
child: Container(
child: FutureBuilder(
future: _loadCSV(),
builder: (BuildContext context,
AsyncSnapshot<List<YoutubeDetail>> snapshot) {
if (snapshot.hasData) {
List<YoutubeDetail> videoDetail = snapshot.data!;
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: videoDetail.length,
itemBuilder: (_, int index) {
if (index > 0) {
return GestureDetector(
child: Container(
height: 80,
child: DisplayVideoView(
videoDetail[index].url,
videoDetail[index].title),
),
onTap: (){
String url = videoDetail[index].url;
String id = url.substring(url.length - 11);
print("HEllo");
_controller.load(id);
// DisplayVideo(id);
}
);
} else {
return Container();
}
});
} else {
return Container();
}
}),
),
),
],
));
}
}
class DisplayVideoView extends StatelessWidget {
String videopath;
String title;
DisplayVideoView(this.videopath, this.title);
#override
Widget build(BuildContext context) {
String url = videopath;
String id = url.substring(url.length - 11);
// TODO: implement build
return Card(
clipBehavior: Clip.antiAlias,
child: Container(
height: 150,
padding: const EdgeInsets.all(0),
child: Row(children: [
Expanded(
flex: 6,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://img.youtube.com/vi/$id/mqdefault.jpg'),
fit: BoxFit.fill)),
),
),
Spacer(
flex: 1,
),
Expanded(
flex: 14,
child: Container(
padding: const EdgeInsets.only(top: 2),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold)),
],
),
),
),
]),
),
);
}
}
What do you think about this approach:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
List<Widget> _myList = [];
void _loadFiveMore() {
_myList = <Widget>[
..._myList,
for (int i = _myList.length; i < _myList.length + 5; i++)
ListTile(title: Text('item $i')),
];
}
#override
void initState() {
_loadFiveMore();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(children: [
..._myList,
OutlinedButton(
onPressed: () {
setState(() => _loadFiveMore());
},
child: const Text('get 5 more'))
]),
),
);
}
}
void main() {
runApp(const ExampleWidget());
}
You can use this package.
have loadmore callback, refresh call back
https://pub.dev/packages/loadmore_listview

How to refresh data after returning to a screen in flutter

I have the following cart screen which contains the address fields. When a user wants to update their address, they click on Update button, which takes them to the address screen where they can update the address. Although when the back button is clicked to return back to the cart screen,the print function shows the updated value but the UI still shows the old value.
class CartScreen extends StatefulWidget {
static const routeName = '/cart';
#override
_CartScreenState createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
Future _addressFuture;
String value = 'deliver';
var address;
void initState() {
_addressFuture = _obtainAddressFuture();
super.initState();
}
Future _obtainAddressFuture() async {
address = await Provider.of<Addresses>(context, listen: false).fetchMyAddress();
}
void didChangeDependencies() async {
print('inside didchange');
super.didChangeDependencies();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
print('Rebuild cart');
return GestureDetector(
child: Scaffold(
appBar: AppBar(
title: Text('Your Cart'),
),
body: FutureBuilder(
future: _addressFuture,
builder: (ctx, dataSnapshot) {
if (dataSnapshot.error != null) {
print('Data snapshot error is ' +
dataSnapshot.error.toString());
return Center(
child: Text('Something went wrong!'),
);
} else if (dataSnapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: Loading(),
);
} else {
return Container(
child: SingleChildScrollView(
child: Column(
children: [
Card(
margin: EdgeInsets.symmetric(
horizontal: 15,
vertical: 4,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
new GestureDetector(
onTap: () async{
_awaitReturnValueFromSecondScreen(context);
},
child: Container(
padding: EdgeInsets.all(10),
child: const Text("Update",
style: TextStyle(
color: Colors.blue))),
)
],
),
TextFormField(
initialValue: address.name,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(Icons.person,
color: Colors.orangeAccent),
labelText: 'Contact name'),
),
],
)),
],
)));
}
})));
}
void _awaitReturnValueFromSecondScreen(BuildContext context) async {
// start the SecondScreen and wait for it to finish with a result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddressScreen('cartScreen'),
));
setState(() {
address = result;
});
print(address.name);
}
}
please check the below example as it requires async and await to get the data from 2nd screen
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Location',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: Provider(
create: (context) => Addresses(),
child: CartScreen(),
),
);
}
}
class CartScreen extends StatefulWidget {
static const routeName = '/cart';
#override
_CartScreenState createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
Future? _addressFuture;
String value = 'deliver';
var address;
void initState() {
_addressFuture = _obtainAddressFuture();
super.initState();
}
Future _obtainAddressFuture() async {
address =
await Provider.of<Addresses>(context, listen: false).fetchMyAddress();
}
void didChangeDependencies() async {
super.didChangeDependencies();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Scaffold(
appBar: AppBar(
title: Text('Your Cart'),
),
body: FutureBuilder(
future: _addressFuture,
builder: (ctx, dataSnapshot) {
if (dataSnapshot.error != null) {
print('Data snapshot error is ' + dataSnapshot.error.toString());
return const Center(
child: Text('Something went wrong!'),
);
} else if (dataSnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
return SingleChildScrollView(
child: Column(
children: [
Card(
margin: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 4,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () async {
// Here you have to wait for the screen to retrun data so we use asyn await
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
AddressScreen(title: 'cartScreen'),
),
);
print(result);
},
child: Container(
padding: EdgeInsets.all(10),
child: const Text(
"Update",
style: TextStyle(
color: Colors.blue,
),
),
),
)
],
),
TextFormField(
// initialValue: address.name,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(Icons.person,
color: Colors.orangeAccent),
labelText: 'Contact name',
),
),
],
),
),
],
),
);
}
},
),
),
);
}
}
class AddressScreen extends StatelessWidget {
const AddressScreen({
Key? key,
required this.title,
}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: TextButton(
onPressed: () {
// Here you have pass the data that you want
Navigator.pop(context, 'Data pass here ');
},
child: Text('Pass data back'),
),
),
);
}
}
class Addresses {
Future fetchMyAddress() async {}
}

Am Trying To calculate total price of a List of items

am making a shopping cart app where I want to calculate the total price of the products present in the cart I made a function for it and tried executing in init state but it's not working
import 'package:flutter/material.dart';
import 'package:shop/Models/Database.dart';
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
int sum = 0;
total() {
studlist.forEach((element) {
sum = sum + element.price;
});
}
#override
void initState() {
total();
super.initState();
}
final DbStudentManager dbmanager = new DbStudentManager();
Student cart;
List<Cart> cartList;
int updateIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Cart',
style: TextStyle(color: Colors.black),
),
),
body: FutureBuilder(
future: dbmanager.getStudentList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
studlist = snapshot.data;
}
return ListView.builder(
itemCount: studlist == null ? 0 : studlist.length,
itemBuilder: (BuildContext context, int index) {
Student ct = studlist[index];
return Card(
child: ListTile(
title: Text(ct.name),
subtitle: Text('${ct.price}'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
dbmanager.deleteStudent(ct.id);
setState(() {
studlist.remove(index);
});
}),
),
);
});
}),
bottomNavigationBar: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Text('$sum'),
SizedBox(
width: 10,
),
Text('Check Out'),
],
),
),
],
),
);
}
}
the error I get:
The method 'forEach' was called on null.
Receiver: null
Tried calling: forEach(Closure: (Student) => Null)
Try this:
import 'package:flutter/material.dart';
import 'package:shop/Models/Database.dart';
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
int sum = 0;
#override
void initState() {
super.initState();
}
final DbStudentManager dbmanager = new DbStudentManager();
Student cart;
List<Cart> studList=[];
int updateIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Cart',
style: TextStyle(color: Colors.black),
),
),
body: FutureBuilder(
future: dbmanager.getStudentList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
studlist = snapshot.data;
studlist.forEach((element) {
setState((){
sum = sum + element.price;
});
});
}
return ListView.builder(
itemCount: studlist == null ? 0 : studlist.length,
itemBuilder: (BuildContext context, int index) {
Student ct = studlist[index];
return Card(
child: ListTile(
title: Text(ct.name),
subtitle: Text('${ct.price}'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
dbmanager.deleteStudent(ct.id);
setState(() {
studlist.remove(index);
});
}),
),
);
});
}),
bottomNavigationBar: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Text('$sum'),
SizedBox(
width: 10,
),
Text('Check Out'),
],
),
),
],
),
);
}
}
Try running total() function if the data when the data is loaded, not in init. As data is initially empty, running it inside init will cause this error.
if (snapshot.hasData) {
studlist = snapshot.data;
total();
}
Full Code:
import 'package:flutter/material.dart';
import 'package:shop/Models/Database.dart';
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
int sum = 0;
total() {
studlist.forEach((element) {
sum = sum + element.price;
});
}
final DbStudentManager dbmanager = new DbStudentManager();
Student cart;
List<Cart> cartList;
int updateIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Cart',
style: TextStyle(color: Colors.black),
),
),
body: FutureBuilder(
future: dbmanager.getStudentList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
studlist = snapshot.data;
total(); //Run the total() function here
}
return ListView.builder(
itemCount: studlist == null ? 0 : studlist.length,
itemBuilder: (BuildContext context, int index) {
Student ct = studlist[index];
return Card(
child: ListTile(
title: Text(ct.name),
subtitle: Text('${ct.price}'),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
dbmanager.deleteStudent(ct.id);
setState(() {
studlist.remove(index);
});
}),
),
);
});
}),
bottomNavigationBar: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Text('$sum'),
SizedBox(
width: 10,
),
Text('Check Out'),
],
),
),
],
),
);
}
}
try this
If I added Quantity increased totalQty but I have one issue if I removing Item in list
did not decrease. sorry my english not good
`total() {
studlist.forEach((element) {
if(element.qty!=null){
totalQty=totalQty+element.qty;
print(totalQty);
}
});
}
`

Can someone check my Dart code and tell me where I'm making mistake in returning data from my screen as a ListView

I am stuck here for the past 20 days in returning data in my app from the other screen. I'm new to programming and need help. I've been searching through all the internet to find an answer related to my query but nothing is helping though. I ask my fellow SO guys to please help.
You can look at the entire code which I've made open here.
My code:
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () async {
final newList = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
setState(() {
return ListView.builder(
itemCount: newList.length,
itemBuilder: (context, index){
return Container(
child: Text('item: $newList'),
);
},
);
});
},
)
],
),
);
}
}
The screen where Navigator.pop() is used:
final Set saved = Set();
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
value: saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// __saved.add(context); // changed
// } else{ // changed
// __saved.remove(context); // changed
// } // changed
if (val == true) {
saved.add(index);
} else {
saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text(nameOfSite[index]),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
),
);
}
}
Here is the SecondPage and FavoriteList that I made
import 'package:flutter/material.dart';
import 'package:aioapp2/lists.dart';
Set<int> favorites = {};
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
_getFavoriteList(),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FloatingActionButton(
child: Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditFavorites(),
),
).then((updatedFavorites) {
if (updatedFavorites != null)
setState(() {
favorites = updatedFavorites;
});
});
},
),
),
)
],
);
}
Widget _getFavoriteList() {
if (favorites?.isNotEmpty == true)
return _FavoriteList();
else
return _EmptyFavoriteList();
}
}
class _EmptyFavoriteList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Add Your Favorite Sites Here!❤',
style: TextStyle(color: Colors.white),
),
Icon(
Icons.favorite,
size: 150,
color: Colors.blue[100],
),
],
),
),
),
),
],
);
}
}
class _FavoriteList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: favorites.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('lib/images/${images[index]}'),
),
title: Text(nameOfSite[favorites.elementAt(index)]),
);
},
);
}
}
//Its FavoriteList Page. I changed the name
class EditFavorites extends StatefulWidget {
#override
_EditFavoritesState createState() => _EditFavoritesState();
}
class _EditFavoritesState extends State<EditFavorites> {
final _editableFavorites = <int>{};
#override
void initState() {
_editableFavorites.addAll(favorites);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red,
actions: <Widget>[
IconButton(
icon: Icon(Icons.done),
onPressed: () {
Navigator.pop<Set>(context, _editableFavorites);
},
)
],
),
//backgroundColor: Colors.indigo,
body: SafeArea(
child: ListView.builder(
itemCount: nameOfSite.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('lib/images/${images[index]}'),
),
title: Text(nameOfSite[index]),
trailing: IconButton(
icon: _editableFavorites.contains(index)
? Icon(
Icons.favorite,
color: Colors.red,
)
: Icon(
Icons.favorite_border,
color: Colors.grey,
),
onPressed: () {
setState(() {
if (_editableFavorites.contains(index))
_editableFavorites.remove(index);
else
_editableFavorites.add(index);
});
},
),
);
},
),
),
);
}
}
Just replace secondtab.dart with this code.
You can copy paste run full code below
You have to move out return ListView to the same layer with FloatingActionButton
working demo
full code
import 'package:flutter/material.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: SecondPage(),
);
}
}
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>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
Set newList = {};
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
ListView.builder(
itemCount: newList.length,
itemBuilder: (context, index) {
return Container(
child: Text('item: ${newList.elementAt(index)}'),
);
},
),
FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () async {
newList = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
setState(() {});
},
)
],
),
);
}
}
final Set saved = Set();
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
value: saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// __saved.add(context); // changed
// } else{ // changed
// __saved.remove(context); // changed
// } // changed
if (val == true) {
saved.add(index);
} else {
saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
//Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text('nameOfSite[index]'),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
),
);
}
}

Flutter Bloc with audioplayers

Almost a day of figuring way how to do it and I need your help.
I've created an app that would have a state with an instance of audioplayers and using flutter_bloc.
Problems:
The state player plays but the Widget MusicPlayer doesn't rebuild the widget with the BlocBuilder
I'm trying also to get the currentPosition and duration of the music I'm playing and displaying it using LinearPercentIndicator from linear_percent_indicator package but can't seem to find a solution because rebuild doesn't work.
What am I missing?
Here's what I have so far:
Bloc
class AudioPlayerBloc extends Bloc<AudioPlayerEvents, MusicPlayerState> {
#override
MusicPlayerState get initialState => MusicPlayerState(
player: AudioPlayer(),
episode: Episode(),
);
#override
Stream<MusicPlayerState> mapEventToState(AudioPlayerEvents event) async* {
if (event is InitializePlayer) {
this.currentState.episode = event.episode;
this.dispatch(PlayPlayer());
yield this.currentState;
}
if (event is PlayPlayer) {
this.play(this.currentState.episode.source);
}
if (event is PlayRemote) {
this.currentState.player.stop();
this.currentState.player.play(event.remoteURL);
yield this.currentState;
}
if (event is ShowPlayer) {
yield this.currentState;
}
if (event is HidePlayer) {
yield this.currentState;
}
}
void play(String remoteURL) {
this.dispatch(PlayRemote(remoteURL));
}
void stop() async {
await this.currentState.player.stop();
}
void pause() async{
await this.currentState.player.pause();
}
void resume(){
this.currentState.player.resume();
}
#override
void dispose() {
super.dispose();
}
}
Event
abstract class AudioPlayerEvents {}
class InitializePlayer extends AudioPlayerEvents {
Episode episode;
InitializePlayer(this.episode);
}
class PlayRemote extends AudioPlayerEvents {
final String remoteURL;
PlayRemote(this.remoteURL);
}
class PlayPlayer extends AudioPlayerEvents {}
class ShowPlayer extends AudioPlayerEvents {}
class HidePlayer extends AudioPlayerEvents {}
State
import 'package:audioplayers/audioplayers.dart';
class MusicPlayerState {
AudioPlayer player;
Episode episode; // My Custom Class
MusicPlayerState({
this.player,
this.episode,
});
}
main.dart
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AudioPlayerBloc>(
builder: (_) => AudioPlayerBloc(),
)
],
child: MaterialApp(
navigatorObservers: [],
initialRoute: HomeScreen.id,
routes: {
HomeScreen.id: (context) => HomeScreen(app: widget.app),
},
),
);
}
}
MusicPlayer.dart <-- my Player Widget
class MusicPlayer extends StatelessWidget {
#override
Widget build(BuildContext context) {
final AudioPlayerBloc audioPlayerBloc =
BlocProvider.of<AudioPlayerBloc>(context);
return BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
bloc: audioPlayerBloc,
builder: (context, state) {
return Container(
height: 200.0,
color: Colors.cyan[200],
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(
children: <Widget>[
Text("${state.episode.name}"),
Row(
children: <Widget>[
Expanded(
flex: 1,
child: FutureBuilder<int>(
future: audioPlayerBloc.currentState.player.getCurrentPosition(),
builder: (context, AsyncSnapshot<int> snapshot) {
double seconds = snapshot.data / 1000;
if (snapshot.hasData) {
return Text("${printDuration(Duration(seconds: seconds.toInt()), abbreviated: true)}");
} else {
return Text('Calculating..');
}
},
),
),
Expanded(
flex: 3,
child: LinearPercentIndicator(
lineHeight: 7.0,
// percent: this.currentPosition / this.fileDuration,
percent: 0.3,
backgroundColor: Colors.grey,
progressColor: Colors.blue,
),
),
Expanded(
flex: 1,
child: FutureBuilder<int>(
future: audioPlayerBloc.currentState.player.getDuration(),
builder: (context, AsyncSnapshot<int> snapshot) {
double seconds = snapshot.data / 1000;
if (snapshot.hasData) {
return Text("${printDuration(Duration(seconds: seconds.toInt()), abbreviated: true)}");
} else {
return Text('Calculating..');
}
},
),
),
],
),
Text(state.player.state.toString()),
FlatButton(
onPressed: () {
print('close here');
Navigator.of(context).pop();
},
child: Icon(
Icons.close,
color: Colors.black.withOpacity(0.5),
),
),
Row(
children: <Widget>[
FlatButton(
onPressed: () {
audioPlayerBloc.pause();
},
child: Text('Pause Player'),
),
FlatButton(
onPressed: () {
audioPlayerBloc.resume();
},
child: Text('Resume Player'),
),
FlatButton(
onPressed: () {
audioPlayerBloc.stop();
},
child: Text('Stop Player'),
),
],
)
],
)
],
),
),
);
},
);
}
}
HomeScreen.dart <-- my first screen
#override
Widget build(BuildContext context) {
final AudioPlayerBloc audioPlayerBloc = BlocProvider.of<AudioPlayerBloc>(context);
return MultiBlocProvider(
providers: [
BlocProvider<AudioPlayerBloc>(
builder: (_) => AudioPlayerBloc(),
)
],
child: Scaffold(
appBar: AppBar(
title: Text('Global Audio Player'),
),
body: Container(
child: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return Column(
children: <Widget>[
Flexible(
child: getListView(context)
),
displayPlayer(state.player), // Here I'm trying to display the player when the AudioPlayerState is PLAYING
],
);
},
),
),
),
);
}
Global function
Widget displayPlayer(AudioPlayer player){
return MusicPlayer();
if(player.state == AudioPlayerState.PLAYING) {
return MusicPlayer();
}
return Container();
}
EpisodesScreen.dart <-- ListView of the Episodes
class _EpisodesScreenState extends State<EpisodesScreen> {
#override
void initState() {
super.initState();
print(widget.series);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.series.name)),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 0,
child: Image.network(widget.series.image.original),
),
Expanded(
child: getListView(context),
),
Expanded(
child: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return displayPlayer(state.player);
},
),
)
],
));
}
Widget getListView(BuildContext context) {
List<Episode> episodesList = widget.series.episodes;
final AudioPlayerBloc audioPlayerBloc =
BlocProvider.of<AudioPlayerBloc>(context);
var listView = ListView.separated(
itemCount: episodesList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(episodesList[index].name),
trailing: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return FlatButton(
onPressed: () {
audioPlayerBloc.dispatch(InitializePlayer(episodesList[index]));
},
child: Icon(
Icons.play_arrow,
color: Colors.black87,
),
);
},
),
);
},
separatorBuilder: (BuildContext context, int index) {
return Divider(
height: 1.0,
color: Colors.black12,
);
},
);
return listView;
}
}
I missed the yield * part of it and it is now working.
Stream<Duration> currentPosition() async* {
yield* this.currentState.audioPlayer.onAudioPositionChanged;
}
Stream<Duration> fileDuration() async* {
yield* this.currentState.audioPlayer.onDurationChanged;
}