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({})
Related
I am trying to display one SingleChildListView containing THREE seperate FutureBuilder with ListView.sperator. I am using Provider for fetching data from the SQFLite database.
Thi is my code:
class SelectCategoryPage extends StatefulWidget {
const SelectCategoryPage({Key? key}) : super(key: key);
#override
State<SelectCategoryPage> createState() => _SelectCategoryPageState();
}
class _SelectCategoryPageState extends State<SelectCategoryPage> {
//
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kBackgroundColor,
appBar: _appBar(),
body: _body(context),
bottomNavigationBar: _bottomNavigationBar(),
);
}
AppBar _appBar() {
return AppBar(
elevation: 0,
backgroundColor: Colors.white,
title: Text(
'Select Category',
style: appBarHeaderTStyle,
),
iconTheme: const IconThemeData(color: Colors.black),
actions: [
Consumer<SelectCategoryViewModel>(
builder: (context, provider, child) {
return TextButton(
child: Text(
provider.getEditOptionData ? 'Save' : 'Edit',
style: textButtonTStyle,
),
onPressed: () {
provider.toggleEditButton();
},
);
},
),
],
);
}
Widget _body(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20, left: 24, right: 24),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
controller: null,
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Text(
'Expense',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_expenseCategory(context),
//
const SizedBox(height: 24),
Text(
'Income',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_incomeCategory(context),
//
const SizedBox(height: 24),
Text(
'Other',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_otherCategory(context),
//
const SizedBox(height: 20),
],
),
),
),
],
),
);
}
FutureBuilder<void> _expenseCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByExpenseType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, expenseProvider, child) => expenseProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: expenseProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: expenseProvider.data[index].id,
coloredIcon: expenseProvider.data[index].categoryIcon,
title: expenseProvider.data[index].categoryName,
isOptionDotsVisitable:
expenseProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _incomeCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByIncomeType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, incomeProvider, child) => incomeProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: incomeProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: incomeProvider.data[index].id,
coloredIcon: incomeProvider.data[index].categoryIcon,
title: incomeProvider.data[index].categoryName,
isOptionDotsVisitable:
incomeProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _otherCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByOtherType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, otherProvider, child) => otherProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: otherProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: otherProvider.data[index].id,
coloredIcon: otherProvider.data[index].categoryIcon,
title: otherProvider.data[index].categoryName,
isOptionDotsVisitable:
otherProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
Widget _bottomNavigationBar() {
return CustomBottomAppBar(
buttonText: '+ Add New Category',
onTapEvent: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CreateNewCategoryPage(),
),
);
},
);
}
}
Only the last FutureBuilder is working properly, and other builders are showing the exact output as the last (3rd) FutureBuilder.
Went through the same thing once. You need to have only one future builder and a Future that gets all the data and assigns it. Something like this:
class _DeviceInformationScreenState extends State<DeviceInformationScreen> {
late StaticConfiguration staticConfiguration;
late PackageInfo packageInfo;
late DeviceData deviceData;
late QuarkusHealthClient quarkusHealth;
LisaInfo lisaInfo = LisaInfo();
//TODO: add translations?
Future<void> _getAppInfo() async {
packageInfo = await PackageInfo.fromPlatform();
staticConfiguration = await config.readStaticConfiguration();
deviceData = await DeviceData.fromDevice();
quarkusHealth = await QuarkusHealthClient.checkHealth(staticConfiguration.grpcServerHost);
lisaInfo = await lisaInfo.getLisaInfo();
}
#override
Widget build(BuildContext context) {
return buildToolsScaffold(context,
body: FutureBuilder(
future: _getAppInfo(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 50.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Text(
T.screens.healthInfo.configData,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20,
),
_buildConfigDataWidgets()
],
),
And _buildConfigDataWidgets() looks like this:
Widget _buildConfigDataWidgets() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(T.screens.healthInfo.data.configFile +
(staticConfiguration.configFileExists
? T.screens.healthInfo.data.exists
: T.screens.healthInfo.data.doesNotExist)),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcHost + ': ' + staticConfiguration.grpcServerHost),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcPort + ': ' + staticConfiguration.grpcServerPort.toString()),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale != null) ...[
Text(T.screens.healthInfo.data.activeLanguageCode + ': ' + staticConfiguration.locale!.languageCode),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale!.countryCode != null)
Text(T.screens.healthInfo.data.activeCountryCode + ': ' + staticConfiguration.locale!.countryCode!),
],
],
);
}
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
},
I used a grid view list in order to show some items in another list that contain images
and doesn't show the items, instead it shows the loading icon
this is my code:
import 'package:flutter/material.dart';
import 'package:sct/list/list.dart';
class badriya2 extends StatefulWidget {
#override
State<badriya2> createState() => _badriya2State();
}
class _badriya2State extends State<badriya2> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
"She codes",
),
),
body: FutureBuilder(builder: (context, AsyncSnapshot snapshot) {
height:
MediaQuery.of(context).size.height;
width:
MediaQuery.of(context).size.width;
if (snapshot.hasData) {
List resList = snapshot.data;
child:
Expanded(
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemCount: resList.length,
itemBuilder: (context, index) {
primary:
true;
padding:
const EdgeInsets.all(20);
shrinkWrap:
true;
children:
<Widget>[
Card(
child: Center(
child: CircleAvatar(
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.asset(
list[0].image,
),
),
minRadius: 50,
maxRadius: 75,
),
),
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
),
];
return Center(child: CircularProgressIndicator());
}));
}
return Center(child: CircularProgressIndicator());
}));
}
}
and this is the list :
import 'package:flutter/cupertino.dart';
List list = [
{
Image.asset('assets/images/butterfly.jpg'),
},
{
Image.asset('assets/images/flower.jpg'),
},
{
Image.asset('assets/images/glass.jpg'),
},
{
Image.asset('assets/images/sun.jpg'),
},
{
Image.asset('assets/images/lighting.jpg'),
},
{
Image.asset('assets/images/phone.jpg'),
},
{
Image.asset('assets/images/eye.jpg'),
},
{
Image.asset('assets/images/photo1.jpg'),
},
];
the point of this code is not to duplicate the items in grid view, I want to write in one line
Add future method onfuture inside FutureBuilder.
return FutureBuilder(
future: yourFutureMethod(),
builder: (context, snapshot) {...},
);
You use the Future Builder but you didn't mention any future. Set the Future
import 'package:flutter/material.dart';
import 'package:sct/list/list.dart';
class badriya2 extends StatefulWidget {
#override
State<badriya2> createState() => _badriya2State();
}
class _badriya2State extends State<badriya2> {
var dummy;
#override
void initState() {
super.initState();
dummy = _getimages();
print("data ${dummy}");
}
_getimages() async {
var imagelist = await list;
print(imagelist);
return imagelist;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
"She codes",
),
),
body: FutureBuilder(
future: _getimages(),
builder: (context, AsyncSnapshot snapshot) {
if(snapshot.hasError) print(snapshot.error);
return snapshot.hasData
?GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
List reslist = snapshot.data;
return Column(
children: [
Card(
child: Center(
child: Container(
width: 100,
height: 100,
child: CircleAvatar(
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.network(reslist[index].toString(),)
),
minRadius: 50,
maxRadius: 75,
),
),
),
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
),
],
);
}
):
Center(
child:CircularProgressIndicator()
);
}
)
);
}
}
And please assign proper list of data
import 'package:flutter/cupertino.dart';
List list= [
"https://dlanzer.com/flutter_api/assets/uploads/images/farm_2021-10-25%2005:09:48am.png",
"https://dlanzer.com/flutter_api/assets/uploads/images/farm_2021-10-25%2005:09:11am.png",
"https://dlanzer.com/flutter_api/assets/uploads/images/farm_2021-10-19%2002:51:18pm.png",
"https://dlanzer.com/flutter_api/assets/uploads/images/farm_2021_10_12_04_30_13_pm.png",
];
Here I use network images You change to asset images
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) {
.....
...
..
}
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