How to implement checkbox over GridView correctly - flutter

I have checkbox for selecting and deselecting photos.
There is a visible loading screen for each tap.
_mediaList has the photo asset. mediaModel has the necessary methods to add and remove the path of selected and deselected photos respectively.
Widget build(BuildContext context) {
super.build(context);
return GridView.builder(
itemCount: _mediaList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, mainAxisSpacing: 4.0, crossAxisSpacing: 4.0),
itemBuilder: (BuildContext context, int index) {
final saved = mediaModel.getMedia().contains(
_mediaList[index].relativePath + '/' + _mediaList[index].title);
return FutureBuilder(
future: _mediaList[index].thumbDataWithSize(200, 200),
builder: (BuildContext context, snapshot) => snapshot.hasData
? GridTile(
header: saved
? Icon(Icons.check_circle, color: Colors.white,)
: Icon(Icons.check_circle_outline, color: Colors.white,),
child: GestureDetector(
child: Image.memory(
snapshot.data,
fit: BoxFit.cover,
),
onTap: () => setState(() => saved
? mediaModel.removeMedia(
_mediaList[index].relativePath +
'/' +
_mediaList[index].title)
: mediaModel.addMedia(
_mediaList[index].relativePath +
'/' +
_mediaList[index].title))),
)
: Container());
},
);
}
EDIT: After some analysis, I found out using Provider to load images might be the right way.
Can you help me in converting this code to Provider?
Thanks in advance!!!

Screenshot:
Full code:
class FooPage extends State<SoPage> {
static const int _count = 10;
final List<bool> _checks = List.generate(_count, (_) => false);
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
itemCount: _count,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (_, i) {
return Stack(
children: [
Container(color: Colors.red[(i * 100) % 900]),
Align(
alignment: Alignment.topCenter,
child: Checkbox(
value: _checks[i],
onChanged: (newValue) => setState(() => _checks[i] = newValue),
),
),
],
);
},
),
);
}
}

Related

I need to change categories layout like this Flutter

I need to change categories layout like this Flutter
Its code of Multi Vendor - Flutter woocommerce app :
code:
class _ShopCategoryScreenState extends State<ShopCategoryScreen> {
void _toShopList(Category cat) {
final model = Provider.of<ShopCategoryModel>(context, listen: false);
model.setCatId(cat.id!);
model.getProducts();
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => ShopProductListScreen.category(
name: cat.name,
ctx: context,
)));
}
Widget _catItem(Category cat) {
return InkWell(
onTap: () => _toShopList(cat),
child: Column(
children: [
Text(
cat.name ?? '',
style: Theme.of(context).textTheme.subtitle1,
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Consumer<ShopCategoryModel>(
builder: (_, model, __) => GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).orientation ==
Orientation.landscape ? 3: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemBuilder: (_, index) => model.isGettingCategories
? _catLoadingItem()
: _catItem(model.categories[index]),
itemCount: model.isGettingCategories ? 5 : model.categories.length,
),
);
}
}
Please help to change above code to same image that i shared ..thanks

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 build only specific objects in the screen

I have built an app that shows images like Pinterest
like that;
And, When I scroll down, after passing 25 images, my build widget refreshs the page and adds new 25 images to the screen and my screen has 50 images and so on. However, this takes me again the top of the screen because of adding new 25 images. because of that, I see again the images that I already see.
And, I want to don't refresh the page again while adding new 25 images but my builder widget works only in this way.
My class file that has builder widget;
class HomeView extends StatefulWidget {
#override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
HomeViewModel viewModel;
#override
Widget build(BuildContext context) {
return BaseView<HomeViewModel>(
onModelReady: (model) {
model.init();
model.setContext(context);
viewModel = model;
},
builder: (context, value) => buildScaffold,
model: HomeViewModel(),
);
}
Scaffold get buildScaffold {
return Scaffold(
body: CustomScrollView(
/* physics: BouncingScrollPhysics(), */
slivers: <Widget>[
buildSliverAppBar,
buildSliverStaggeredGrid,
],
),
);
}
SliverAppBar get buildSliverAppBar {
return SliverAppBar(
floating: true,
pinned: true,
snap: false,
backgroundColor: Colors.white,
title: buildSearchField,
actions: <Widget>[buildIconButtonClose, buildanotherbutton],
);
}
TextField get buildSearchField {
return TextField(
controller: viewModel.searchController,
decoration: InputDecoration(hintText: "Search...", border: InputBorder.none),
onSubmitted: (String keyword) => viewModel.loadImages(keyword),
);
}
IconButton get buildIconButtonClose {
return IconButton(
onPressed: () => viewModel.resetImages(),
icon: Icon(Icons.close, color: Colors.black),
);
}
IconButton get buildanotherbutton {
return IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Passaword()),
);
},
icon: Icon(Icons.ac_unit, color: Colors.black),
);
}
Widget get buildSliverStaggeredGrid {
return SliverPadding(
padding: EdgeInsets.all(0),
sliver: Observer(builder: (_) {
return viewModel.loadingImages
? SliverToBoxAdapter(
child: Center(child: CircularProgressIndicator()))
: SliverStaggeredGrid.countBuilder(
crossAxisCount: 2,
itemCount: viewModel.images.length,
itemBuilder: (BuildContext context, int index) =>
buildImageTile(index),
crossAxisSpacing: 5,
mainAxisSpacing: 10,
staggeredTileBuilder: (int index) {
return buildStaggeredTile(viewModel.images[index]);
},
);
}),
);
}
StaggeredTile buildStaggeredTile(UnsplashImage image) {
double aspectRatio = image.height / image.width;
double columnWidth = MediaQuery.of(context).size.width / 2;
return StaggeredTile.extent(1, aspectRatio * columnWidth);
}
Widget buildImageTile(int index) {
return FutureBuilder(
future: viewModel.loadImage(index),
builder: (context, snapshot) => ImageTileWidget(image: snapshot.data),
);
}
}
If you want my whole code, I can share with you. I thought my file that has builder widget code is enough to solve the problem.
my builder widget here;
Widget get buildSliverStaggeredGrid {
return SliverPadding(
padding: EdgeInsets.all(0),
sliver: Observer(builder: (_) {
return viewModel.loadingImages
? SliverToBoxAdapter(
child: Center(child: CircularProgressIndicator()))
: SliverStaggeredGrid.countBuilder(
crossAxisCount: 2,
itemCount: viewModel.images.length,
itemBuilder: (BuildContext context, int index) =>
buildImageTile(index),
crossAxisSpacing: 5,
mainAxisSpacing: 10,
staggeredTileBuilder: (int index) {
return buildStaggeredTile(viewModel.images[index]);
},
);
}),
);
}

how to create an icon on image with inkwell after Click on the image in flutter

I made a grid view in the flutter app. But like the Pictures on the below link , I want to create an icon on the picture and change the background color After tap the picture,
I've been looking for ways, but I've finally got a question. I'd appreciate it from the bottom of my heart if you'd let me know.
Please enter img link(below)
https://firebasestorage.googleapis.com/v0/b/instaclone-2-fd9de.appspot.com/o/post%2F12344.png?alt=media&token=89d46c03-83ba-4d30-b716-e9b718c1340b
Widget _bodyBuilder() {
// TODO : 그 예시를 어떻해 stream View로 보여줄것인가
return StreamBuilder <QuerySnapshot>(
stream: _commentStream(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(!snapshot.hasData){
return Center(child: CircularProgressIndicator());
}
var items = snapshot.data?.documents ??[];
var fF = items.where((doc)=> doc['style'] == "오피스룩").toList();
var sF = items.where((doc)=> doc['style'] == "로맨틱").toList();
var tF = items.where((doc)=> doc['style'] == "캐주").toList();
fF.addAll(sF);
fF.addAll(tF);
fF.shuffle();
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.6,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0),
itemCount: fF.length,
itemBuilder: (BuildContext context, int index) {
return _buildListItem(context, fF[index]);
});
},
);
}
Widget _buildListItem(context, document) {
return Ink.image(
image : NetworkImage(document['thumbnail_img']),
fit : BoxFit.cover,
child: new InkWell(
//I think we need to get something in here....
onTap: (){},
),
);
}
You should create List of Image which have isSelected value and when the user clicks on item them set true/false base of the old value which have imageURL and isSelected variable. First, you should store value in List Of Image obj. which coming from Firebase/API then flow below step. I have created a demo and post here. Please take reference.
Example code
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
List<ImageData> imageList;
#override
void initState() {
super.initState();
imageList = ImageData.getImage();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 0.5,
crossAxisCount: 5,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0),
itemCount: imageList.length,
itemBuilder: (builder, index) {
return InkWell(
onTap: () {
setState(() {
imageList[index].isSelected = !imageList[index].isSelected;
});
},
child: Stack(
children: [
_getImage(imageList[index].imageURL),
Opacity(
opacity: imageList[index].isSelected ? 1 : 0,
child: Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
color: Colors.black38,
),
Center(
child: CircleAvatar(
backgroundColor: Colors.greenAccent,
child: Icon(
Icons.check,
color: Colors.white,
),
),
)
],
),
)
],
));
},
),
);
}
_getImage(url) => Image.network(
url,
height: 500,
fit: BoxFit.fitHeight,
);
#override
void dispose() {
super.dispose();
}
}
class ImageData {
String imageURL;
bool isSelected;
int id;
ImageData(this.imageURL, this.isSelected, this.id);
static List<ImageData> getImage() {
return [
ImageData('https://picsum.photos/200', false, 1),
ImageData('https://picsum.photos/100', false, 2),
ImageData('https://picsum.photos/300', false, 3),
ImageData('https://picsum.photos/400', false, 4),
ImageData('https://picsum.photos/500', false, 5),
ImageData('https://picsum.photos/600', false, 6),
ImageData('https://picsum.photos/700', false, 7),
ImageData('https://picsum.photos/800', false, 8),
ImageData('https://picsum.photos/900', false, 9),
];
}
}
Output

How to use Mobx for List object in Flutter?

I want to change the status of checkbox via use Mobx, after the click the value have changed but not update on UI, Observer don't see change action that it is rebuilding this Widget
In file Mobx:
#observable
List<bool> statusCheckbox = List<bool>();
#computed
List<bool> get getStatusCheckBox {
return statusCheckbox;
}
#action
changeStatusItem(bool val, int index) {
statusCheckbox[index] = val;
}
In file contain Widget:
Observer(
name: 'ListHomePage',
builder: (BuildContext context) {
return child: GridView.builder(
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: (itemWidth / itemHeight),
),
itemCount:
_userManagementStore.userAPI.users.length,
itemBuilder: (context, index) {
UserManagement user =
_userManagementStore.getUser(index: index);
return AnimationConfiguration.staggeredGrid(
position: index,
duration:
const Duration(milliseconds: 375),
columnCount: 2,
child: Container(
child: ScaleAnimation(
child: GestureDetector(
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.topRight,
child: Checkbox(
value: _userManagementStore.getStatusCheckBox[index],
onChanged: (bool val) {
_userManagementStore.changeStatusItem(val, index);
},
),
),
],
),
},
)
I can't find the root cause of the issue
As said Antoan Elenkov, you should remove the #computed property.
But you main problem is that you should not using a List but an ObservalbleList, it works like a List.
So, you have to replace
List<bool> statusCheckbox = List<bool>();
by
#observable
ObservableList<bool> statusCheckbox = ObservableList();