I try to place an element above another element in flutter. With transform: Matrix4.translationValues it worked to set a negative value, but the element above has a bigger z-index. How could I adjust that? To understand what I need:
This is what I have
This is what I need
My code
class _AlbumDetailState extends State<AlbumDetail> {
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, int>;
final albumID = routeArgs['id'];
final index = routeArgs['index'];
final picturesData = Provider.of<Pictures>(context, listen: true);
Future<void> _addPictureToGallery() async {
final picker = ImagePicker();
final imageFile =
await picker.getImage(source: ImageSource.gallery, maxWidth: 600);
final appDir = await syspath.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile.path);
final savedImage =
await File(imageFile.path).copy('${appDir.path}/$fileName');
print(savedImage);
picturesData.add(Picture(
album: albumID, path: savedImage.path, timestamp: Timestamp.now()));
}
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text("Album"),
flexibleSpace: FlexibleSpaceBar(
background: Container(
color: Colors.transparent,
child: Hero(
tag: "open_gallery" + index.toString(),
child: Image(
image: NetworkImage('https://placeimg.com/640/480/any'),
fit: BoxFit.cover,
),
),
)),
expandedHeight: 350,
backgroundColor: Colors.green,
pinned: true,
stretch: false,
),
SliverToBoxAdapter(
child: FutureBuilder(
future: picturesData.getPicturesFromAlbum(albumID),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data.length == 0) {
return Center(
child: Text("Noch keine Bilder vorhanden"),
);
}
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
alignment: Alignment.center,
transform: Matrix4.translationValues(0.0, -75.0, 0.0),
width: MediaQuery.of(context).size.width - 50,
height: 150,
color: Colors.black87,
margin: EdgeInsets.only(top: 50),
child: Text(
"Headline",
style: Theme.of(context)
.textTheme
.headline2
.copyWith(color: theme.colorScheme.onPrimary),
),
),
StaggeredGridView.countBuilder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 6,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) =>
Container(
child: Image.file(
File(snapshot.data[index].path),
fit: BoxFit.cover,
)),
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 2 : 1),
mainAxisSpacing: 5.0,
crossAxisSpacing: 5.0,
),
],
);
}
}),
)
],
),
);
}
}
The problem: The z-index is not correct on my element. My header is above. How could I adjust the z-index? I know this from CSS. Is there a way to to this with flutter?
One way of achieving overlapping widgets is by using Stack widget. You can check the docs for more details.
try this package https://pub.dev/packages/indexed
https://raw.githubusercontent.com/physia/kflutter/main/indexed/doc/assets/demo.gif
This package allows you to order items inside stack using index like z-index in css
this is example how it works
Indexer(
children: [
Indexed(
index: 100,
child: Positioned(
//...
)
),
Indexed(
index: 1000,
child: Positioned(
//...
)
),
Indexed(
index: 3,
child: Positioned(
//...
)
),
],
);
if you are using bloc of some complex widget you can extands or implement the IndexedInterface class and override index getter:
class IndexedDemo extends IndexedInterface {
int index = 5;
}
or implements
class IndexedDemo extends AnimatedWidget implements IndexedInterface {
int index = 1000;
//...
//...
}
then use it just like Indexed class widget:
Indexer(
children: [
IndexedDemo(
index: 100,
child: Positioned(
//...
)
),
IndexedFoo(
index: 1000,
child: Positioned(
//...
)
),
],
);
Online demo
Video demo
Related
I am currently modifying my app to support large devices. In this app I want to have a list of categories at the left and when I tap the list tile I want to show the list of the items which the category holds on the left. By the approach I am using the list updates in memory but doesnt change the UI. This is my current approach ->
class _OrderCategoryScreenState extends State<OrderCategoryScreen> {
List<FoodItem> filteredLists = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: [
ValueListenableBuilder<Box<FoodCategory>>(
valueListenable: Boxes.getFoodCategories().listenable(),
builder: (context, box, child) {
final categories = box.values.toList().cast<FoodCategory>();
return categories.length == 0
? Container(
color: Color(0xFFFCF9F9),
height: double.infinity,
width: MediaQuery.of(context).size.height * 0.30,
child: Center(
child: Text("No Categories"),
))
: Container(
height: double.infinity,
width: MediaQuery.of(context).size.height * 0.35,
child: Drawer(
backgroundColor: Color(0xFFFCF9F9),
elevation: 0,
child: ListView(
children: <Widget>[
DrawerHeader(
child: ListView(
children: [
Container(
height: MediaQuery.of(context).size.height,
child: ValueListenableBuilder<
Box<FoodCategory>>(
builder: (context, value, child) {
final foodCategories = box.values
.toList()
.cast<FoodCategory>();
return ListView.builder(
itemCount: foodCategories.length,
itemBuilder: (context, index) {
return ListTile(
tileColor: Colors.green,
onTap: () {
filteredLists = Boxes
.getFoodItems()
.values
.where((element) =>
element.categoryName ==
foodCategories[index]
.name)
.toList();
print(filteredLists.length);
print("object");
},
title: Text(
foodCategories[index].name),
);
},
);
},
valueListenable: Boxes.getFoodCategories()
.listenable(),
),
)
],
),
),
],
),
));
},
),
Container(
width: MediaQuery.of(context).size.width * 0.5,
height: MediaQuery.of(context).size.height * 0.5,
color: Colors.amberAccent,
child: filteredLists.isEmpty
? Center(
child: Text("Choose A Category"),
)
: ListView.builder(itemCount: filteredLists.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(filteredLists[index].itemName),
);
},
),
),
],
)
),
);
}
}
When I hot reload using this approach it works on the UI as well.
Please Help.
Try calling setState to update the UI.
onTap: () {
filteredLists = Boxes
.getFoodItems()
.values
.where((element) =>
element.categoryName ==
foodCategories[index]
.name)
.toList();
print(filteredLists.length);
print("object");
setState((){}); //this
},
When I transition to a screen where I get a list of information via an API, it initially gives an error:
_CastError (Null check operator used on a null value)
and only after loading the information, the screen is displayed correctly.
I am declaring the variables like this:
#observable
ObservableFuture<Model?>? myKeys;
#action
getKeys() {
myKeys = repository.getKeys().asObservable();
}
How can I enter the page only after loading the information?
In button action I tried this but to no avail!
await Future.wait([controller.controller.getKeys()]);
Modular.to.pushNamed('/home');
This is the page where the error occurs momentarily, but a short time later, that is, when the api call occurs, the data appears on the screen.
class MyKeyPage extends StatefulWidget {
const MyKeyPage({Key? key}) : super(key: key);
#override
State<MyKeyPage> createState() => _MyKeyPageState();
}
class _MyKeyPageState
extends ModularState<MyKeyPage, KeyController> {
KeyController controller = Modular.get<KeyController>();
Widget countKeys() {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
final count =
controller.myKeys?.value?.data!.length.toString();
if (snapshot.connectionState == ConnectionState.none &&
!snapshot.hasData) {
return Text('..');
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return Text(count.toString() + '/5');
});
},
future: controller.getCountKeys(),
);
}
#override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return controller.getCountKeys() != "0"
? TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Keys',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
countKeys(),
],
),
),
),
body: Observer(builder: (_) {
return Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: ListView.builder(
padding: EdgeInsets.only(
left: 12.0,
top: 2.0,
right: 12.0,
),
itemCount:
controller.myKeys?.value?.data!.length,
itemBuilder: (context, index) {
var typeKey = controller
.myKeys?.value?.data?[index].type
.toString();
var id =
controller.myKeys?.value?.data?[index].id;
final value = controller
.myKeys?.value?.data?[index].value
.toString();
return GestureDetector(
onTap: () {
.
.
},
child: CardMeyKeys(
typeKey: typeKey,
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
myKeys: pixController
.minhasChaves?.value?.data?[index].type
.toString(),
),
);
},
),
),
);
}),
bottomSheet: ....
)
: TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'...',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
],
),
),
),
body: Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/images/Box.png',
fit: BoxFit.cover,
width: 82.75,
height: 80.91,
),
SizedBox(
height: 10,
),
],
),
), //Center
),
),
bottomSheet: ...
);
}
List<ReactionDisposer> disposers = [];
#override
void initState() {
super.initState();
controller.getKeys();
}
#override
void dispose() {
disposers.forEach((toDispose) => toDispose());
super.dispose();
}
}
Initially the error occurs in this block
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
_CastError (Null check operator used on a null value)
I appreciate if anyone can help me handle ObservableFuture correctly!
You need to call the "future" adding
Future.wait
(the return type of getKeys) keys=await Future.wait([
controller.getKeys();
]);
The problem is your getKeys function isn't returning anything, so there's nothing for your code to await. You need to return a future in order to await it.
Future<Model?> getKeys() {
myKeys = repository.getKeys().asObservable();
return myKeys!; // Presumably this isn't null anymore by this point.
}
...
await controller.controller.getKeys();
Modular.to.pushNamed('/home');
I created a PageView with a fixed value but it is an issue. How i am gonna convert this to dynamic height? SizedBox is in a Column's child. I tried Expanded and Flexible widgets but they did not work.
class BranchViewBottomSection extends ConsumerWidget {
const BranchViewBottomSection({
Key? key,
required this.data,
required this.branchId,
required this.companyId,
}) : super(key: key);
final Map<String, dynamic> data;
final String branchId;
final String companyId;
#override
Widget build(BuildContext context, WidgetRef ref) {
return Column(
children: [
SizedBox(
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: data['available_sections'].length,
itemBuilder: (BuildContext context, int index) {
if (data['available_sections'][index]["branchTabValue"] ==
ref.read(tabIndexProvider)) {
return BranchSectionBox(
data: data, index: index, isActive: true);
} else {
return BranchSectionBox(
data: data, index: index, isActive: false);
}
},
),
],
),
),
GestureDetector(
onTap: () {
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
MenuView(
branchId: branchId,
companyId: companyId,
branchData: data,
),
transitionsBuilder:
(context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.ease;
final tween = Tween(begin: begin, end: end);
final curvedAnimation = CurvedAnimation(
parent: animation,
curve: curve,
);
return SlideTransition(
position: tween.animate(curvedAnimation),
child: child,
);
},
),
);
},
child: Container(
height: 50,
width: MediaQuery.of(context).size.width,
color: Colors.pink,
child: const Center(
child: Text(
"Sipariş vermek için dokunun.",
style: TextStyle(color: Colors.white),
),
),
),
),
Expanded(
child: PageView(
onPageChanged: (v) {
ref.watch(tabIndexProvider.notifier).state =
data['available_sections'][v]['branchTabValue'];
},
children: <Widget>[
BranchViewHomePage(),
BranchViewDetailsPage(),
const BranchViewCommentsPage(),
BranchViewContactPage(),
],
),
)
],
);
}
}
This is the parent.
// ignore_for_file: non_constant_identifier_names, file_names
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:neshapp/comps/common/MainCircularProgressIndicator.dart';
import 'package:neshapp/services/FirestoreService.dart';
import 'package:neshapp/utils/constants.dart';
import '../../providers/BranchViewProviders.dart';
import '../../providers/MenuProviders.dart';
import 'BranchSectionBoxes.dart';
class BranchView extends ConsumerWidget {
final String branchId;
final String companyId;
final String tableNo;
const BranchView(
{Key? key,
required this.branchId,
required this.companyId,
required this.tableNo})
: super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
/*
Uygulamanın her yerinde kullanabilmek için eğer bir şubeye
girilirse şubenin ve markanın id'lerini providerlara veriyorum.
*/
ref.watch(branchIdProvider.notifier).setId(branchId);
ref.watch(companyIdProvider.notifier).setId(companyId);
ref.watch(tableNoProvider.notifier).setTable(tableNo);
return SafeArea(
child: Scaffold(
body: FutureBuilder<DocumentSnapshot>(
future: FirestoreService.getBranchData(companyId, branchId),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final data = snapshot.data?.data() as Map<String, dynamic>;
return CustomScrollView(
scrollBehavior: const ScrollBehavior(),
slivers: <Widget>[
SliverAppBar(
elevation: 0,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("${data['branch_name']} Şubesi"),
FutureBuilder<DocumentSnapshot>(
future: FirestoreService.getCompanyData(companyId),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
final CData =
snapshot.data?.data() as Map<String, dynamic>;
return Container(
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: constsColor.black.withOpacity(0.5),
),
child: Image.network(CData['company_logo']),
);
} else {
return const MainCircularProgressIndicator();
}
},
),
],
),
pinned: true,
expandedHeight: 200,
backgroundColor: constsColor.neshMoru,
flexibleSpace: FlexibleSpaceBar(
background: AspectRatio(
aspectRatio: 16 / 9,
child: Stack(
children: [
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(data['branch_image']),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 10,
right: 10,
child: Text(
tableNo,
style: TextStyle(
color: constsColor.white,
fontSize: 18,
),
),
),
],
),
),
),
),
SliverToBoxAdapter(
child: BranchViewBottomSection(
data: data,
branchId: branchId,
companyId: companyId,
),
),
],
);
} else {
return const Center(
child: MainCircularProgressIndicator(),
);
}
},
),
),
);
}
}
expand and flexible i believe are both depend on the parten widget.
how about using the state for that ?
I have horizontal ListView.builder and CupertinoSliverRefreshControl, so when it reaches the end, I want to display Loading indicator, but for some reason I am getting error
Null check operator used on a null value
The relevant error-causing widget was
CustomScrollView
lib/sliver_loading.dart:19
The most unclear part is that CupertinoSliverRefreshControl works fine with Vertical ListView.builder, but when I change Axis on horizontal it rises this above error.
Here is a code :
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15),
child: CustomScrollView(
scrollDirection: Axis.horizontal, // Here is when Error rise
slivers: [
SliverToBoxAdapter(
child: SizedBox(
height: 200,
child: ListView.builder(
scrollDirection: Axis.horizontal,
primary: false,
shrinkWrap: true,
itemCount: 4,
itemBuilder: (context, index) {
return Container(
width: 100,
height: 200,
color: colors[index],
);
},
),
),
),
CupertinoSliverRefreshControl(
onRefresh: () async {
await Future.delayed(Duration(seconds: 3));
print('loaded');
},
),
],
),
),
);
}
Can anyone explain me, why is this happening and what are the solutions?
There is a workaround with current snippet instead of using CupertinoSliverRefreshControl return row with loading widget for last item. Also wrap Container with Center.
itemBuilder: (context, index) {
return index == 13 // items length-1
? Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.cyanAccent,
),
CircularProgressIndicator(),
],
)
:Center( child: Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.amber,
));
},
If you do use ListView, you can use ScrollController with listener and get position to load data using controller.position.maxScrollExtent* .9 ;load more on 90% scroll.
Also, using the same directional multi-scrollabe widgets is not necessary. We can skip using ListView and use SliverList. While the width is fixed, we can compare the items' length and current scroll position to using the controller.
final ScrollController controller = ScrollController();
#override
void initState() {
super.initState();
controller.addListener(() {
print(controller.offset);
//14 total item , I am using 90%
if (controller.offset > 100 * 14 * .9) {
// you may encounter multiple call use another flag or null to handle this
print("load more");
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15),
child: CustomScrollView(
scrollDirection: Axis.horizontal,
controller: controller,
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => index == 13 // items length-1
? Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.cyanAccent,
),
CircularProgressIndicator(),
],
)
: Center(
child: Container(
margin: EdgeInsets.all(20),
width: 100,
height: 200,
color: Colors.amber,
)),
childCount: 14,
),
),
],
),
),
);
}
}
Okay, so here is a way how I solved this problem. Since CupertinoSliverRefreshControl does not work with horizontal ListView.builder, I decided to use CupertinoActivityIndicator and CupertinoActivityIndicator.partiallyRevealed.
When ListView reaches to the end, I am calculating distance between ListView.builder() and int distance and updating double progress for CupertinoActivityIndicator.partiallyRevealed, next when progress reaches 1.0 I just replace CupertinoActivityIndicator.partiallyRevealed with CupertinoActivityIndicator changing bool isActive value to true.
Finally it works like CupertinoSliverRefreshControl, just without slivers :).
Code Example
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HorizontalLoader extends StatefulWidget {
const HorizontalLoader({Key? key}) : super(key: key);
static final colors = [
Colors.red,
Colors.indigoAccent,
Colors.purple,
Colors.amberAccent,
Colors.orange,
Colors.purple,
Colors.cyanAccent,
Colors.red,
Colors.indigoAccent,
Colors.purple,
];
#override
State<HorizontalLoader> createState() => _HorizontalLoaderState();
}
class _HorizontalLoaderState extends State<HorizontalLoader> {
int distance = 70; // offset
bool isActive = false;
double progress = 0.0;
// Base logic. you can also use this logic with ScrollController()
bool _handleNotification(ScrollNotification notify) {
double outRangeLoading = distance + notify.metrics.maxScrollExtent;
double currentPixel = notify.metrics.pixels;
if (notify.metrics.extentAfter <= 0.0) {
if (currentPixel >= outRangeLoading) {
networkLoader();
}
calculateProgress(outRangeLoading, currentPixel);
}
return true;
}
// Some math
void calculateProgress(outRangeLoading, currentPixel) {
double current, currentAsPrecent;
current = outRangeLoading - currentPixel;
currentAsPrecent = (100 * current) / distance;
setState(() {
progress = (100 - currentAsPrecent) * 0.01;
});
}
// To simulate loading data from Network
void networkLoader() async {
isActive = true;
await Future.delayed(Duration(seconds: 3));
isActive = false;
setState(() {
progress = 0.0;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 200, bottom: 200),
child: Stack(
children: [
Positioned(
right: 15,
top: 210,
child: isActive
? CupertinoActivityIndicator()
: CupertinoActivityIndicator.partiallyRevealed(
progress: progress,
),
),
NotificationListener<ScrollNotification>(
onNotification: _handleNotification,
child: ListView.builder(
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
itemCount: HorizontalLoader.colors.length + 1,
itemBuilder: (context, index) {
if (index == HorizontalLoader.colors.length) {
return isActive ? SizedBox(width: 50) : SizedBox();
}
return Container(
width: 100,
height: 100,
color: HorizontalLoader.colors[index],
);
},
),
),
],
),
),
);
}
}
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({})