How to load more items to a list when reach the bottom of results flutter - flutter

I have the code below which feed a list with 10 results from firebase. In this case it shows only the 10 items, now I wanna, when user gets the bottom of results, it loads more 10 items and add it to the list. I already have the scrollController and it works.. I receive the log "LOAD HERE" when I get the bottom of the results.
My doubt is how to add the new 10 items in the list?
scrollListener() async {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
print('LOAD HERE');
}
}
#override
void initState() {
scrollController.addListener(scrollListener);
super.initState();
}
#override
void dispose() {
scrollController.removeListener(scrollListener);
super.dispose();
}
loadList(submenu ,callback, context, deviceSize){
return FutureBuilder(
future: ctrlLab.loadList(submenu, 10),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.error != null) {
print(snapshot.error);
return Center(child: Text('ERROR!'));
}else {
return GridView.builder(
padding: EdgeInsets.all(10.0),
controller: scrollController,
itemCount: snapshot.data.length,
itemBuilder: (ctx, i) {
Item item = snapshot.data[i];
if (i < snapshot.data.length) {
return Dismissible(
key: UniqueKey(),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.all(10.0),
color: Colors.grey[800],
child: Align(
alignment: AlignmentDirectional.centerEnd,
child: Icon(
Icons.delete,
color: Colors.white,
size: 40,
),
),
),
onDismissed: (DismissDirection direction) {
ctrl.onDismissed(callback, item);
},
child: GestureDetector(
child: Card(
elevation: 5.0,
child: Padding(
padding: EdgeInsets.all(10.0),
child: GridTile(
child: Hero(
tag: "${item}",
child: item.imageUrl == null
? setIconLab(item)
: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: setIconLab(item),
placeholder: (ctx, url) =>
Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Image.asset('assets/images/noPhoto.jpg',
fit: BoxFit.cover),
),
),
footer: Container(
padding: EdgeInsets.all(8.0),
color: Colors.white70,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
item.name
),
),
],
),
),
),
),
),
),
);
}
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndLoading(
itemCount: snapshot.data.length + 1,
crossAxisCount: deviceSize.width < 600 ? 2 : 3,
childAspectRatio: 0.7,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
),
);
}
},
);
}

Infinite Scrolling in ListView
I have achieved this case by using the local field instead of getting data from firebase. Hope it will give you some idea.
import 'package:flutter/material.dart';
class ListViewDemo extends StatefulWidget {
ListViewDemo({Key key}) : super(key: key);
#override
_ListViewDemoState createState() => _ListViewDemoState();
}
class _ListViewDemoState extends State<ListViewDemo> {
ScrollController controller;
int count = 15;
#override
void initState() {
super.initState();
controller = ScrollController()..addListener(handleScrolling);
}
void handleScrolling() {
if (controller.offset >= controller.position.maxScrollExtent) {
setState(() {
count += 10;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List view'),
),
body: ListView.builder(
controller: controller,
itemCount: count,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
),
);
}
#override
void dispose() {
controller.removeListener(handleScrolling);
super.dispose();
}
}

You have to add another 10 data to the crtLap.loadList(subMenu, 20) and call setState inside the scrollListener to rebuild the widget about the changes.
var data = crtLap.loadList(subMenu, 10);
scrollListener() async {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
setState((){
data = crtLap.loadList(subMenu, 20);
});
}
}
and use this data field to the FutureBuilder directly,
loadList(submenu ,callback, context, deviceSize){
return FutureBuilder(
future: data,
builder: (ctx, snapshot) {
.....
...
..
}

Related

flutter listview builder inside a listview builder

I don't have much experience with flutter.
I would like to use the language_tool library (https://pub.dev/packages/language_tool) for Dart and Flutter.
To show the data obtained from the tool() function, I created a FutureBuilder with a ListView.builder inside, which returns a Column.
I would like there to be 2 children inside the column:
1- a Text with mistake.issueDescription as text (for each "mistake")
2- another ListView that returns the elements of the List mistake.replacements for each "mistake"
Anyone know how I can fix it?
Below I put the code I created, which works fine until I put the Listview builder inside the first ListView builder.
import 'package:flutter/material.dart';
import 'package:language_tool/language_tool.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele';
Future<List<WritingMistake>> tool(String text) async {
var tool = LanguageTool();
var result = tool.check(text);
var correction = await result;
List<WritingMistake> mistakes = [];
for (var m in correction) {
WritingMistake mistake = WritingMistake(
message: m.message,
offset: m.offset,
length: m.length,
issueType: m.issueType,
issueDescription: m.issueDescription,
replacements: m.replacements,
);
mistakes.add(mistake);
}
print(mistakes.length);
print(mistakes);
return mistakes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return Column(
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),
);
}
}
I hope I was clear and that someone can help me.
Thank you :)
You cannot give a listview-builder as a child for a column try changing the Column widget to a ListView and set its shrinkWrap property to true.
ListView(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return ListView(
shrinkWrap:true,
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),

How to create a custom gridview with appropriate aspect ratio?

I am trying to create a custom image picker that looks something like this:
As you can see the images are capped at a set height however maintain their aspect ratio (i.e. vertical images are vertical and horizontal images are horizontal). I have created the entire custom image picker. However, I'm struggling with the aspect ratio part. I'd prefer not to use a library, however, if it's easier then please provide the adjusted code.
Could you please provide a solution with code?
FYI I'm using photo_manager to retrieve the images/videos.
This is what it all looks like right now:
Here is my code:
class MediaGrid extends StatefulWidget {
#override
_MediaGridState createState() => _MediaGridState();
}
class _MediaGridState extends State<MediaGrid> {
List<Widget> _mediaList = [];
int currentPage = 0;
int? lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
_fetchNewMedia() async {
lastPage = currentPage;
var result = await PhotoManager.requestPermission();
if (result) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(currentPage, 60);
print(media);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder<dynamic>(
future: asset.thumbDataWithSize(300, 300),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return Stack(
children: <Widget>[
Expanded(
child: Image.memory(snapshot.data, fit: BoxFit.cover),
),
if (asset.type == AssetType.video)
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam_rounded,
color: Colors.white,
),
),
),
],
);
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
/// if result is fail, you can call `PhotoManager.openSetting();` to open android/ios applicaton's setting to get permission
}
}
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
return _handleScrollEvent(scroll);
},
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: _mediaList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 5.0,
crossAxisSpacing: 5.0,
),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
);
}
}
This idea will only work if you wish to give each image a single specified height. And using fit: BoxFit.cover to fill up the remaining space.
Now you must find a way to get the width of each image, in my code its of Network Images
From here use the width as the flex value.
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
Future<Size> _calculateImageDimension(String url) {
Completer<Size> completer = Completer();
Image image = Image.network(url);
image.image.resolve(ImageConfiguration()).addListener(
ImageStreamListener(
(ImageInfo image, bool synchronousCall) {
var myImage = image.image;
Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
completer.complete(size);
},
),
);
return completer.future;
}
#override
Widget build(BuildContext context) {
//for odd no. of images you might have to add more conditions to your widget
final _netWorkimages = [
'https://images.pexels.com/photos/7179053/pexels-photo-7179053.jpeg?cs=srgb&dl=pexels-olya-prutskova-7179053.jpg&fm=jpg',
'https://images.pexels.com/photos/7527509/pexels-photo-7527509.jpeg?cs=srgb&dl=pexels-john-lee-7527509.jpg&fm=jpg',
'https://images.pexels.com/photos/8018591/pexels-photo-8018591.jpeg?cs=srgb&dl=pexels-inna-stellinna-8018591.jpg&fm=jpg',
'https://images.pexels.com/photos/3244513/pexels-photo-3244513.jpeg?cs=srgb&dl=pexels-andy-vu-3244513.jpg&fm=jpg',
'https://images.pexels.com/photos/694587/pexels-photo-694587.jpeg?cs=srgb&dl=pexels-samuel-silitonga-694587.jpg&fm=jpg',
'https://images.pexels.com/photos/5121986/pexels-photo-5121986.jpeg?cs=srgb&dl=pexels-marcelo-chagas-5121986.jpg&fm=jpg',
'https://images.pexels.com/photos/4519234/pexels-photo-4519234.jpeg?cs=srgb&dl=pexels-dinielle-de-veyra-4519234.jpg&fm=jpg',
'https://images.pexels.com/photos/2286385/pexels-photo-2286385.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
'https://images.pexels.com/photos/35629/bing-cherries-ripe-red-fruit.jpg?cs=srgb&dl=pexels-pixabay-35629.jpg&fm=jpg',
'https://images.pexels.com/photos/4033324/pexels-photo-4033324.jpeg?cs=srgb&dl=pexels-karolina-grabowska-4033324.jpg&fm=jpg'
];
List<Future<Size>> _niSizes = [];
_netWorkimages.forEach((url) {
_niSizes.add(_calculateImageDimension(url));
});
return FutureBuilder<List<Size>>(
future: Future.wait(_niSizes),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(
child: CircularProgressIndicator(),
);
else
return ListView.builder(
itemCount: _netWorkimages.length - 1,
itemBuilder: (context, i) {
return i.isEven
? Container(
height: 120,
child: Row(
children: [
Flexible(
flex: snapshot.data![i].width.toInt(),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Image(
image: NetworkImage(_netWorkimages[i]),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
),
)),
Flexible(
flex: snapshot.data![i + 1].width.toInt(),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Image(
image: NetworkImage(_netWorkimages[i + 1]),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
),
)),
],
),
)
: SizedBox.shrink();
});
},
);
}
}
I hope this is thing you are looking for, replace container with image.
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'dart:math' as math;
class TestScreen extends StatefulWidget {
TestScreen({Key? key}) : super(key: key);
#override
_TestScreenState createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final _items = List.generate(
100,
(index) => ClipRRect(
borderRadius: BorderRadius.circular(
8,
),
child: Container(
height: 124,
color: Color(
(math.Random().nextDouble() * 0xFFFFFF).toInt(),
).withOpacity(1.0),
),
));
#override
Widget build(BuildContext context) {
return Container(
child: StaggeredGridView.countBuilder(
mainAxisSpacing: 2,
crossAxisSpacing: 2,
crossAxisCount: 6,
itemCount: 100,
itemBuilder: (context, index) {
return _items[index];
},
staggeredTileBuilder: (index) {
if (index % 6 == 0 || index % 6 == 3) {
return StaggeredTile.count(2, 1);
} else if (index % 6 == 1 || index % 6 == 2) {
return StaggeredTile.count(4, 1);
} else
return StaggeredTile.count(3, 1);
},
),
);
}
}
You can use https://pub.dev/packages/flutter_staggered_grid_view
This plugin has options to modify the aspect ratio

Flutter Application – Empty GridTile and conditions

I have a list of some categories and recipes (food recipes app). So, I have two questions:
Why I see empty space, when open the Category? I think it gives SizedBox(), but I can't write null at this place, because of crash.
Did I make the correct condition for checking a recipe for a category?
Recipes Tab
class RecipesTab extends StatefulWidget {
#override
_RecipesTabState createState() => _RecipesTabState();
}
class _RecipesTabState extends State<RecipesTab> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
initialIndex: 1,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text('Книга рецептов'),
bottom: TabBar(
isScrollable: true,
indicatorPadding: EdgeInsets.all(10),
indicatorColor: Color(0xFF0FA3B1),
labelColor: Color(0xFF0FA3B1),
tabs: [
Tab(text: 'Избранное'),
Tab(text: 'Категории'),
],
),
),
body: Padding(
padding: const EdgeInsets.only(top: 5.0),
child: TabBarView(
children: [
Favorite(),
Categories(),
],
),
),
),
);
}
}
Categories
class Categories extends StatefulWidget {
#override
_CategoriesState createState() => _CategoriesState();
}
class _CategoriesState extends State<Categories> {
Future<List<Category>> _getCategories() async {
var data = await http.get(
"https://ibragimov.xyz:7000/categories/?format=json",
headers: {'Content-Type': 'application/json; charset=utf-8'},
);
var jsonData = json.decode(utf8.decode(data.bodyBytes));
List<Category> categories = [];
for (var i in jsonData) {
Category category = Category(i['id'], i['name']);
categories.add(category);
}
return categories;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getCategories(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Center(
child: CircularProgressIndicator(),
),
),
);
} else {
return Container(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int id) {
return Card(
elevation: 0,
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
title: Text(snapshot.data[id].name),
trailing: Icon(Icons.arrow_forward_ios_rounded),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Recipes(snapshot.data[id])));
},
),
);
},
),
);
}
},
);
}
}
class Category {
final int id;
final String name;
Category(this.id, this.name);
}
Recipes
class Recipes extends StatefulWidget {
final Category category;
Recipes(this.category);
#override
_RecipesState createState() => _RecipesState();
}
class _RecipesState extends State<Recipes> {
Future<List<Recipe>> _getRecipes() async {
var data = await http.get("https://ibragimov.xyz:7000/recipes/?format=json",
headers: {'Content-Type': 'application/json; charset=utf-8'});
var jsonData = json.decode(utf8.decode(data.bodyBytes));
List<Recipe> recipes = [];
for (var i in jsonData) {
Recipe recipe =
Recipe(i['id'], i['name'], i['cost'], i["category"], i["photo"]);
recipes.add(recipe);
}
return recipes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.category.name),
),
body: FutureBuilder(
future: _getRecipes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Center(
child: CircularProgressIndicator(),
),
),
);
} else {
return Container(
child: GridView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int id) {
return snapshot.data[id].category == widget.category.name
? Card(
elevation: 0,
margin:
EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: GridTile(
child: Image.network(
'https://2recepta.com/recept/omlet-s-pomidorami/omlet-s-pomidorami.jpg',
fit: BoxFit.cover,
height: 100,
width: 100,
),
footer: Text(snapshot.data[id].name),
header: Text('data'),
),
)
: SizedBox.shrink();
},
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
),
);
}
},
),
);
}
}
class Recipe {
final int id;
final String name;
final int cost;
final String category;
final String photo;
Recipe(this.id, this.name, this.cost, this.category, this.photo);
}
Screenshots:
Categories
Chinese kitchen
Russian kitchen

Flutter page jumps to top after setState({})

I display many images in a Staggered Gridview in a Flutter application.
Everytime I call setState({}), for example after deleting an item, the page jumps to top. How could I remove this behavior?
This is my code:
final _scaffoldKey = new GlobalKey<ScaffoldState>();
.. outside the build function. And then...
return loadingScreen == true
? LoadingScreen()
: Scaffold(
key: _scaffoldKey,
body: CustomScrollView(
slivers: <Widget>[
_AppBar(
theme: theme,
index: index,
albumImagePath: albumImagePath,
albumID: albumID,
albumValue: albumValue,
addPictureToGallery: _addPictureToGallery,
),
SliverToBoxAdapter(
child: Column(
children: <Widget>[
InfoBar(
albumPicturesSum: albumPicturesSum,
getBilderString: _getBilderString,
theme: theme,
getVideoProgress: _getVideoProgress,
progress: progress,
),
albumID == 99999999
? // Demo Projekt
DemoImageGrid(
demoImageList: demoImageList,
getDemoImagesJson: _getDemoImagesJson,
)
: UserImageGrid(
picturesData: picturesData,
albumID: albumID,
showPictureActions: _showPictureActions)
],
),
)
],
),
);
}
The UserImageGrid looks like the following:
class UserImageGrid extends StatelessWidget {
final Pictures picturesData;
final int albumID;
final Function showPictureActions;
final _key = new UniqueKey();
UserImageGrid(
{#required this.picturesData,
#required this.albumID,
#required this.showPictureActions});
#override
Widget build(BuildContext context) {
return FutureBuilder(
key: _key,
future: picturesData.getPicturesFromAlbum(albumID),
builder: (BuildContext context, AsyncSnapshot snapshot) {
// Normale Projekte
if (snapshot.hasData && snapshot.data.length == 0) {
return Center(
child: Column(
children: <Widget>[
Lottie.asset('assets/lottie/drone.json',
width: 250,
options: LottieOptions(enableMergePaths: false)),
],
),
);
}
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Container(
child: StaggeredGridView.countBuilder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(0),
crossAxisCount: 6,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) =>
GestureDetector(
onLongPress: () {
showPictureActions(snapshot.data[index]);
},
onTap: () async {
await showDialog(
context: context,
builder: (_) {
return Dialog(
child: Stack(
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0,
),
height: 500.0,
child: ClipRect(
child: PhotoView(
maxScale:
PhotoViewComputedScale.covered * 2.0,
minScale:
PhotoViewComputedScale.contained *
0.8,
initialScale:
PhotoViewComputedScale.covered,
imageProvider: FileImage(
File(snapshot.data[index].path))),
),
),
Positioned(
bottom: 20,
left: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
DateFormat(tr("date_format")).format(
snapshot.data[index].timestamp
.toDateTime()),
style: TextStyle(color: Colors.white),
),
),
)
],
));
});
},
child: Container(
child: Image.file(
File(snapshot.data[index].thumbPath),
fit: BoxFit.cover,
)),
),
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 2 : 2),
mainAxisSpacing: 5.0,
crossAxisSpacing: 5.0,
),
);
}
});
}
}
What could be the issue?
I found a solution for this issue. The problem was not the setState({}). It was the return Widget of the FutureBuilder.
I changed
if (!snapshot.hasData || snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
to:
if (!snapshot.hasData || snapshot.connectionState == ConnectionState.waiting) {
return Container(
height: MediaQuery.of(context).size.height,
);
}
I don´t exactly know why, but with this change the page is not jumping to top anymore on setState({})

problems with video player flutter

I'm new in Flutter and try to load video from api, but video player is not working correctly. I have PageView in my Widget _buildFilmsMainPages() and each Page will get own video from api.
i'm getting error while data is loading: The following NoSuchMethodError was thrown building FutureBuilder>(dirty, state: _FutureBuilderState>#033d3):
The method '[]' was called on null.
Receiver: null
Tried calling:
After loading this error is gone.
when i tap on play (Floating Action Button) i see only first frame of video but sound is working.
when i tap on stop (Floating Action Button) first frame of video is gone but sound is still working.
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_ HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
bool isVideo = false;
int filmIndex = 0;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold (
body: SingleChildScrollView(
child: _buildFilmsMainPages(),
)
);
}
Widget _buildFilmsMainPages() =>
FutureBuilder(
future:getMoviesListByDay(date))),
builder: (BuildContext context, AsyncSnapshot snapshot) {
without this code i get error, but player isn't working
correctly anyway and i'm getting error while data is loading
_controller= VideoPlayerController.network
(snapshot.data[filmIndex].media!=null?
snapshot.data[filmIndex].media.elementAt(1)
:"https://flutter.github.io/assets-for-api-
docs/assets/videos/butterfly.mp4");
_initializeVideoPlayerFuture =_controller.initialize();
_controller.setLooping(true);
if (snapshot.data != null) {
return
Column(
children:<Widget> [
SizedBox(
height: 460.0,
child: PageView.builder(
pageSnapping: true,
itemCount: snapshot.data.length,
onPageChanged: (int index) {
setState(() {
filmIndex = index;
i thought i should write this code here
_controller = VideoPlayerController.network
(snapshot.data[filmIndex].media!=null?
snapshot.data[filmIndex].media.elementAt(1)
:"https://flutter.github.io/assets-for-api-
docs/assets/videos/butterfly.mp4");
_initializeVideoPlayerFuture =
_controller.initialize();
_controller.setLooping(true);
});
},
itemBuilder: (context, index) {
return Column(
children: <Widget>[
Container(margin: const
EdgeInsets.symmetric(horizontal: 16.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Container(
height: 250.0,
child: Stack(children: <Widget>[
Container(
child: GestureDetector(
child: isVideo ?
FutureBuilder(
future:_initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
return AspectRatio(
aspectRatio: 16.0 / 12.0,
child:VideoPlayer(_controller),
);
} else {
return Center(
child:
CircularProgressIndicator());
}
},
) :
FadeInImage.assetNetwork(
placeholder:
"assets/placeholder.jpg",
image:
snapshot.data[index].media!=null
? snapshot.data[index].media.elementAt(0): "",
fit: BoxFit.fill,
fadeInDuration:Duration(milliseconds: 50),
),
onTap: () =>
)
),
)
)
),
Row(children: <Widget>[
Container(
margin: const EdgeInsets.only(left: 16.0,
right: 16.0, top: 190.0),
height: 40.0,
width: 40.0,
child: FittedBox(
child: FloatingActionButton(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
onPressed: () {
setState(() {
isVideo = !isVideo;
if (_controller.value.isPlaying) {
_controller.pause();
} else {
_controller.play();
}
});
},
child: Icon(_controller.value.isPlaying
? Icons.pause : LineIcons.play,
size: 30.0,)
),
)
);