fetch url from data file to redirect to new pointed page in gridviewbuilder - flutter

New to flutter and coding so newbie warning :D, I'm trying to create a gridview.builder that will on tap redirect to correctly assigned to that index page (that is already created) but I don't know how to assign it in model.dart and then to pass it to Navigator in InkWell, would greatly appreciate any explanation how this works
this is gridview page
import 'package:flutter/material.dart';
import 'package:practice/data/model.dart';
import 'package:practice/builds/allbuilds.dart';
class GridViewPage extends StatefulWidget {
#override
_GridViewPage createState() => _GridViewPage();
}
class _GridViewPage extends State<GridViewPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey,
automaticallyImplyLeading: true,
title: const Text(
'Builds',
style: TextStyle(
fontFamily: 'Lexend Doca',
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
actions: const [],
centerTitle: false,
elevation: 2,
),
body: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.fromSTEB(15, 0, 15, 25),
child: GridView.builder(
itemCount: griddata.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 1,
),
itemBuilder: (context, index) {
return GridSingleItem(itemGriddata: griddata[index]);
},
),
),
),
],
));
}
}
class GridSingleItem extends StatelessWidget {
final dynamic itemGriddata;
const GridSingleItem({Key key, #required this.itemGriddata})
: super(key: key);
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () async {
Navigator.push(
context, MaterialPageRoute(builder: (context) => PageX()));
},
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: const Color(0x00EEEEEE),
image: DecorationImage(
fit: BoxFit.cover,
image: Image.asset(itemGriddata["image"]).image,
),
),
),
),
);
}
}
and this is model
const griddata = [
{
"name": "Page1",
"image" : "assets/images/Image1.png",
},
{
"name": "Page2",
"image" : "assets/images/Image2.png",
}
];

If I got your question right.
You want to create a model for a particular data,
and then use the data in gridviewbuilder,
and then pass the data to the next screen/page when tapped. [
{
"name": "Page1",
"image" : "assets/images/Image1.png",
},
{
"name": "Page2",
"image" : "assets/images/Image2.png",
}
]
from the data we can have
class Data{
final String name;
final String image;
Data({this.name,this.image});
}
Now const griddata becomes;
const griddata = [
Data(name:'Page1',image:"assets/images/Image1.png",),
Data(name: "Page2","image : "assets/images/Image2.png")
];
What we've done so far is create a model from the raw gridData,
Now passing the data to other pages;
I'll recommend making the destination page i.e PageX to accept Data model
like this;
class PageX extends StatelessWidget{
final Data data; //<------
PageX(data);
#override
Widget build(){
....
}
}
Then in your onTap property, you can have someting like;
Navigator.push(
context, MaterialPageRoute(builder: (context) => PageX(data)));
UPDATE:
class GridSingleItem extends StatelessWidget {
final dynamic itemGriddata;
final Function onTap; //add a function as a parameter/property here
const GridSingleItem({Key key,
#required this.itemGriddata,
this.onTap,
this.image,
})
: super(key: key);
#override
Widget build(BuildContext context) {
return InkWell(
onTap:onTap,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: const Color(0x00EEEEEE),
image: DecorationImage(
fit: BoxFit.cover,
image: Image.asset(itemGriddata["image"]).image,
),
),
),
),
);
}
}
FULL CODE :
import 'package:flutter/material.dart';
import 'package:practice/data/model.dart';
import 'package:practice/builds/allbuilds.dart';
class GridViewPage extends StatefulWidget {
#override
_GridViewPage createState() => _GridViewPage();
}
class _GridViewPage extends State<GridViewPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.grey,
automaticallyImplyLeading: true,
title: const Text(
'Builds',
style: TextStyle(
fontFamily: 'Lexend Doca',
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
actions: const [],
centerTitle: false,
elevation: 2,
),
body: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.fromSTEB(15, 0, 15, 25),
child: GridView.builder(
itemCount: griddata.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 1,
),
itemBuilder: (context, index) {
return GridSingleItem(itemGriddata:
griddata[index],
onTap:(){
Navigator.push(
context, MaterialPageRoute(builder: (context) =>
PageX(griddata[index])));
}
);
},
),
),
),
],
));
}
}

ok I figured it out myself, turns out its literally 2 lines of code
in model, swap const to final, add url property
final griddata = [
{
"name": "Page1",
"image" : "assets/images/Image1.png",
"url": Page1()
},
{
"name": "Page2",
"image" : "assets/images/Image2.png",
"url": Page2()
}
];
and in GridSingleItem onTap pass itemGriddata["url"]
class GridSingleItem extends StatelessWidget {
final dynamic itemGriddata;
const GridSingleItem({Key key, #required this.itemGriddata})
: super(key: key);
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () async {
Navigator.push(
context, MaterialPageRoute(builder: (context) => itemGriddata["url"]));
},

Related

Deleting Item out of List, Listview.build shows wrong data

I have a Stateful widget that i pass a list to (for example 2 items).
After I delete an item, the widget should rebuild itself.
Unfortunately, the deleted item is still displayed and the other one is not.
When I re-enter the widget, the correct item is loaded.
There is a similar problem List not updating on deleting item
but maybe someone can explain me what i did wrong and why provider is helping me here instead of setState?
My code is:
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:trip_planner/util/dialog_box.dart';
import 'package:trip_planner/util/previewUrl.dart';
class BookingPage extends StatefulWidget {
final List toDoList;
BookingPage({
super.key,
required this.toDoList,
});
#override
State<BookingPage> createState() => _BookingPageState();
}
class _BookingPageState extends State<BookingPage> {
//text controller
final _controller = TextEditingController();
final _database = FirebaseDatabase.instance.ref();
//Liste is an example what i have in my list
List toDoList2 = [
["https://www.booking.com/Share-Rnv2Kf", true],
["https://www.booking.com/Share-3hKQ0r", true],
];
void initState(){
super.initState();
}
void deleteTask(int index){
setState(() {
widget.toDoList.removeAt(index);
});
//DatabaseReference _testRef = _database.child("Hotel:");
//_testRef.set(widget.toDoList.toString());
}
//save new Item
void saveNewItem(){
setState(() {
widget.toDoList.add([_controller.text, false]);
//DatabaseReference _testRef = _database.child("Hotel:");
//_testRef.set(widget.toDoList.toString());
_controller.clear();
});
Navigator.of(context).pop();
}
void createNewItem(){
showDialog(
context: context,
builder: (context){
return DialogBox(
controller: _controller,
onSave: saveNewItem,
onCancel: () => Navigator.of(context).pop(),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Booking Seiten'),
elevation: 0,
),
floatingActionButton: FloatingActionButton(
onPressed: createNewItem,
child: Icon(Icons.add),
),
body: ListView.builder(
itemCount: widget.toDoList.length,
itemBuilder: (context, index){
return PreviewUrl(
url2: widget.toDoList[index][0],
deleteFunction: (context) => setState(() => deleteTask(index)),
);
},
),
);
}
}
i thought setState does the same thing as when i re-enter the widget, but it doesn't.
import 'package:any_link_preview/any_link_preview.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:url_launcher/url_launcher.dart';
class PreviewUrl extends StatelessWidget {
final String url2;
//Function(bool?)? onChanged;
Function(BuildContext)? deleteFunction;
PreviewUrl({
super.key,
required this.url2,
required this.deleteFunction,
//required this.onChanged,
});
Future openBrowserURL({
required String url,
bool inApp = false,
}) async {
if(await canLaunch(url)){
await launch(
url,
forceSafariVC: inApp, //iOS
forceWebView: inApp, //Android
enableJavaScript: true, //Android
);
}
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(1.0),
child: Slidable(
endActionPane: ActionPane(
motion: StretchMotion(),
children: [
SlidableAction(
onPressed: deleteFunction,
icon: Icons.delete,
backgroundColor: Colors.red.shade300,
borderRadius: BorderRadius.circular(12),
)
],
),
child: Container(
child: AnyLinkPreview.builder(
link: url2,
itemBuilder: (context, metadata, imageProvider) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (imageProvider != null)
GestureDetector(
onTap: () async {
final url = url2;
openBrowserURL(url: url, inApp: true);
},
child: Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.width *0.25,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12)),
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
Container(
width: double.infinity,
color: Theme.of(context).primaryColor.withOpacity(0.6),
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (metadata.title != null)
Text(
metadata.title!,
maxLines: 1,
style:
const TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 5),
if (metadata.desc != null)
Text(
metadata.desc!,
maxLines: 1,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
metadata.url ?? url2,
maxLines: 1,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
),
),
),
),
);
}
}
If you run the simplified version of your code in DartPad - it will work:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
List toDoList = [
["Button 1", true],
["Button 2", true],
];
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: BookingPage(toDoList: toDoList),
),
),
);
}
}
class BookingPage extends StatefulWidget {
final List toDoList;
const BookingPage({
super.key,
required this.toDoList,
});
#override
State<BookingPage> createState() => _BookingPageState();
}
class _BookingPageState extends State<BookingPage> {
//Liste is an example what i have in my list
List toDoList2 = [
["Button 1", true],
["Button 2", true],
];
#override
void initState() {
super.initState();
}
void deleteTask(int index) {
setState(() {
widget.toDoList.removeAt(index);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Booking Seiten'),
elevation: 0,
),
body: ListView.builder(
itemCount: widget.toDoList.length,
itemBuilder: (context, index) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.lightBlue,
padding: const EdgeInsets.all(12),
textStyle: const TextStyle(fontSize: 22),
),
child: Text(widget.toDoList[index][0]!),
onPressed: () => setState(() => deleteTask(index)),
);
},
),
);
}
}
Which tells me that the problem is your PreviewUrl. My guess is - it is a statful widget, and when the tree rebuilds - it will link the old State object to the first item.
Using Keys might help, something like:
return PreviewUrl(
key: ObjectKey(widget.toDoList[index]),
url2: widget.toDoList[index][0],
deleteFunction: (context) => setState(() => deleteTask(index)),
);

Can someone show me what i am missing in my code? I am trying to display sub-category as a dropdown in category screen in flutter using extension tile

can someone help me figure out what i'm doing wrong? I have a category screen and I have a list-view of all my parent categories on the left and my main categories filling the remaining space as a drop-down widget to display the sub-categories but the expansion tile is not opening. Below is the code of my category screen
`
import 'package:buyfast/Widget/category/main_category_widget.dart';
import 'package:buyfast/models/category_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
class CategoryScreen extends StatefulWidget {
const CategoryScreen({Key? key}) : super(key: key);
#override
State<CategoryScreen> createState() => _CategoryScreenState();
}
class _CategoryScreenState extends State<CategoryScreen> {
String _title = 'Categories';
String? selectedCategory;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(
selectedCategory==null ? _title : selectedCategory!,
style: const TextStyle(color: Colors.black,fontSize: 16),),
elevation: 0,
backgroundColor: Colors.white,
iconTheme: const IconThemeData(
color: Colors.black54
),
actions: [
IconButton(
onPressed: (){},
icon: const Icon(IconlyLight.search),
),
IconButton(
onPressed: (){},
icon: const Icon(IconlyLight.buy),
),
IconButton(
onPressed: (){},
icon: const Icon(Icons.more_vert),
),
],
),
body: Row(
children: [
Container(
width: 80,
color: Colors.grey.shade300,
child: FirestoreListView<Category>(
query: categoryCollection,
itemBuilder: (context, snapshot) {
Category category = snapshot.data();
return InkWell(
onTap: (){
setState(() {
_title= category.catName!;
selectedCategory = category.catName;
});
},
child: Container(
height: 70,
color: selectedCategory == category.catName ? Colors.white : Colors.grey.shade300,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 30,
child: CachedNetworkImage(
imageUrl: category.image!,
color: selectedCategory == category.catName ? Theme.of(context).primaryColor:Colors.grey.shade700,
),
),
Text(
category.catName!,
style: TextStyle(
fontSize: 10,
color: selectedCategory == category.catName ? Theme.of(context).primaryColor:Colors.grey.shade700,
),
textAlign: TextAlign.center,
),
],
),
),
),
),
);
},
),
),
MainCategoryWidget(
selectedCat: selectedCategory,
)
],
),
);
}
}
Now my category model to retrieve the categories from Firebase
import 'package:buyfast/firebase_service.dart';
class Category {
Category({this.catName, this.image});
Category.fromJson(Map<String, Object?> json)
: this(
catName: json['catName']! as String,
image: json['image']! as String,
);
final String? catName;
final String? image;
Map<String, Object?> toJson() {
return {
'catName': catName,
'image': image,
};
}
}
FirebaseService _service = FirebaseService();
final categoryCollection = _service.categories.where('active',isEqualTo: true).withConverter<Category>(
fromFirestore: (snapshot, _) => Category.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),
);
My category widget
import 'package:buyfast/models/category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
class CategoryWidget extends StatefulWidget {
const CategoryWidget({Key? key}) : super(key: key);
#override
State<CategoryWidget> createState() => _CategoryWidgetState();
}
class _CategoryWidgetState extends State<CategoryWidget> {
String? _selectedCategory;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: [
const SizedBox(height: 18,),
const Padding(
padding: EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Stores For You',
style: TextStyle(
fontWeight: FontWeight.bold,
letterSpacing: 1,
fontSize: 20
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(8,0,8,8),
child: SizedBox(
height: 40,
child: Row(
children: [
Expanded(
child:FirestoreListView<Category>(
scrollDirection: Axis.horizontal,
query: categoryCollection,
itemBuilder: (context, snapshot) {
Category category = snapshot.data();
return Padding(
padding: const EdgeInsets.only(right: 4),
child: ActionChip(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2)
),
backgroundColor: _selectedCategory == category.catName ? Colors.blue.shade900 : Colors.grey,
label: Text(
category.catName!,
style: TextStyle(
fontSize: 12,
color: _selectedCategory==category.catName ? Colors.white : Colors.black
),
),
onPressed: () {
setState(() {
_selectedCategory = category.catName;
});
},
),
);
},
),
),
Container(
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.grey.shade400),)
),
child: IconButton(
onPressed: (){
},
icon: const Icon(IconlyLight.arrowDown),
),
)
],
),
),
),
],
),
);
}
}
main category widget
import 'package:buyfast/Widget/category/sub_category_widget.dart';
import 'package:buyfast/models/main_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class MainCategoryWidget extends StatefulWidget {
final String? selectedCat;
const MainCategoryWidget({this.selectedCat,Key? key}) : super(key: key);
#override
State<MainCategoryWidget> createState() => _MainCategoryWidgetState();
}
class _MainCategoryWidgetState extends State<MainCategoryWidget> {
#override
Widget build(BuildContext context) {
return Expanded(
child: FirestoreListView<MainCategory>(
query: mainCategoryCollection(widget.selectedCat),
itemBuilder: (context, snapshot) {
MainCategory mainCategory = snapshot.data();
return ExpansionTile(
title: Text(mainCategory.mainCategory!),
children: [
SubCategoryWidget(
selectedSubCat: mainCategory.mainCategory,
)
],
);
},
),
);
}
}
main category model
import 'package:buyfast/firebase_service.dart';
class MainCategory {
MainCategory({this.category, this.mainCategory});
MainCategory.fromJson(Map<String, Object?> json)
: this(
category: json['category']! as String,
mainCategory: json['mainCategory']! as String,
);
final String? category;
final String? mainCategory;
Map<String, Object?> toJson() {
return {
'category': category,
'mainCategory': mainCategory,
};
}
}
FirebaseService _service = FirebaseService();
mainCategoryCollection (selectedCat){
return _service.mainCategories.where('approved',isEqualTo: true).where('category', isEqualTo: selectedCat).withConverter<MainCategory>(
fromFirestore: (snapshot, _) => MainCategory.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),);
}
`
Subcategory model
`
import 'package:buyfast/firebase_service.dart';
class SubCategory {
SubCategory({this.mainCategory, this.subCatName, this.image});
SubCategory.fromJson(Map<String, Object?> json)
: this(
mainCategory: json['mainCategory']! as String,
subCatName: json['subCatName']! as String,
image: json['image']! as String,
);
final String? mainCategory;
final String? subCatName;
final String? image;
Map<String, Object?> toJson() {
return {
'mainCategory': mainCategory,
'subCatName': subCatName,
'image': image,
};
}
}
FirebaseService _service = FirebaseService();
subCategoryCollection({selectedSubCat}){
return _service.subCategories.where('active',isEqualTo: true).where('mainCategory',isEqualTo: selectedSubCat).withConverter<SubCategory>(
fromFirestore: (snapshot, _) => SubCategory.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),
);
}
`
Subcategory widget
`
import 'package:buyfast/models/sub_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class SubCategoryWidget extends StatelessWidget {
final String? selectedSubCat;
const SubCategoryWidget({this.selectedSubCat,Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: FirestoreQueryBuilder<SubCategory>(
query: subCategoryCollection(
selectedSubCat: selectedSubCat
),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: snapshot.docs.length == 0 ? 1/.1 : 1/1.1,
),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
SubCategory subCat = snapshot.docs[index].data();
return InkWell(
onTap: (){
//move to product screen
},
child: Column(
children: [
SizedBox(
height: 60,
width: 60,
child: FittedBox(
fit: BoxFit.contain,
child: Image.network(subCat.image!)),
),
Text(subCat.subCatName!,style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
},
);
},
),
);
}
}
`
I solved it by wrapping the Expanded widget in the sub_category_widget.dart file with a SizeBox and give it a height of 100.
Like below.
import 'package:buyfast/models/sub_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class SubCategoryWidget extends StatelessWidget {
final String? selectedSubCat;
const SubCategoryWidget({this.selectedSubCat,Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizeBox(height: 100,
Expanded(
child: FirestoreQueryBuilder<SubCategory>(
query: subCategoryCollection(
selectedSubCat: selectedSubCat
),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: snapshot.docs.length == 0 ? 1/.1 : 1/1.1,
),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
SubCategory subCat = snapshot.docs[index].data();
return InkWell(
onTap: (){
//move to product screen
},
child: Column(
children: [
SizedBox(
height: 60,
width: 60,
child: FittedBox(
fit: BoxFit.contain,
child: Image.network(subCat.image!)),
),
Text(subCat.subCatName!,style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
},
);
},
),
),
);
}
}

How to wait for async method results in flutter?

Currently me and my team members are working in a Mobile application. These application contains five pages. In all the five pages we have a bottom drawer to display the list of apps. Actually we are using flutter's installed_apps package for displaying the list of installed apps. But the main problem here is...in order to avoid the code redundancy we refactor the bottom drawer code into a separate bottom_drawer_class widget. But the problem here is if we use a separate file, the async service method return the empty list of apps. if we embedd the bottom_drawer_class inside the main dart file, it is working finely. We dont know how to get rid of it.
Our code:
if we not refactor the main.dart file
import 'package:bottom_drawer/bottom_drawer.dart';
import 'package:flutter/material.dart';
import 'dart:typed_data';
import 'package:installed_apps/installed_apps.dart';
import 'package:installed_apps/app_info.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final double _headerHeight = 60.0;
final double _bodyHeight = 300.0;
final BottomDrawerController _controller = BottomDrawerController();
List listApps = [];
#override
void initState() {
super.initState();
_getApp();
}
void _getApp() async {
List<AppInfo> apps = await InstalledApps.getInstalledApps(true, true);
for (var app in apps) {
var item = AppModel(
title: app.name!,
package: app.getVersionInfo(),
icon: app.icon!,
);
listApps.add(item);
}
print(listApps.length);
//reloading state
setState(() {});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Bottom drawer example app'),
),
body: Stack(
children: [
_buildBottomDrawer(context),
],
),
),
);
}
Widget _buildBottomDrawer(BuildContext context) {
return BottomDrawer(
header: _buildBottomDrawerHead(context),
body: _buildBottomDrawerBody(context),
headerHeight: _headerHeight,
drawerHeight: _bodyHeight,
color: Colors.white,
controller: _controller,
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
spreadRadius: 3,
offset: Offset(5, -2), // changes position of shadow
),
],
);
}
Widget _buildBottomDrawerHead(BuildContext context) {
return Container(
height: _headerHeight,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(45)),
),
child: Column(
children: const [
Padding(
padding: EdgeInsets.only(
left: 0.0,
right: 160.0,
top: 27.0,
),
child: Text(
"Most frequently used apps",
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
fontFamily: 'inter'),
),
),
Spacer(),
Divider(
height: 0.1,
thickness: 4,
color: Colors.black12,
),
],
),
);
}
Widget _buildBottomDrawerBody(BuildContext context) {
return SizedBox(
width: double.infinity,
height: _bodyHeight,
child: ListView.builder(
itemCount: listApps.length,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (BuildContext context, int i) => Column(
children: [
Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
width: .1,
),
),
),
child: ListTile(
leading: Image.memory(listApps[i].icon),
title: Text(listApps[i].title),
subtitle: Text(listApps[i].package),
trailing: const Icon(Icons.timer),
visualDensity: const VisualDensity(vertical: -1),
),
),
],
),
),
);
}
}
class AppModel {
final String title;
final String package;
final Uint8List icon;
AppModel({required this.title, required this.package, required this.icon});
}
it will give the desired list of results. But if we refactor the code in separate file the list remains empty.
refactored files
main.dart
import 'package:flutter/material.dart';
import 'bottom_drawer_class.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Bottom drawer example app'),
),
body: Stack(
children: const [
BottomDrawerGeneral(),
],
),
),
);
}
}
bottom_drawer_class.dart
import 'package:flutter/material.dart';
import 'package:bottom_drawer/bottom_drawer.dart';
import 'dart:typed_data';
import 'package:installed_apps/installed_apps.dart';
import 'package:installed_apps/app_info.dart';
class BottomDrawerGeneral extends StatefulWidget {
const BottomDrawerGeneral({Key? key}) : super(key: key);
#override
State<BottomDrawerGeneral> createState() => BottomDrawerGeneralState();
}
class BottomDrawerGeneralState extends State<BottomDrawerGeneral> {
final double _headerHeight = 60.0;
final double _bodyHeight = 300.0;
final BottomDrawerController _controller = BottomDrawerController();
List listApps = [];
void _getApp() async {
List<AppInfo> apps = await InstalledApps.getInstalledApps(true, true);
for (var app in apps) {
var item = AppModel(
title: app.name!,
package: app.getVersionInfo(),
icon: app.icon!,
);
listApps.add(item);
}
//reloading state
}
#override
void initState() {
super.initState();
_getApp();
}
#override
Widget build(BuildContext context) {
return BottomDrawer(
header: _buildBottomDrawerHead(context),
body: _buildBottomDrawerBody(context),
headerHeight: _headerHeight,
drawerHeight: _bodyHeight,
color: Colors.white,
controller: _controller,
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
spreadRadius: 3,
offset: Offset(5, -2), // changes position of shadow
),
],
);
}
Widget _buildBottomDrawerHead(BuildContext context) {
return Container(
height: _headerHeight,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(45)),
),
child: Column(
children: const [
Padding(
padding: EdgeInsets.only(
left: 0.0,
right: 160.0,
top: 27.0,
),
child: Text(
"Most frequently used apps",
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
fontFamily: 'inter'),
),
),
Spacer(),
Divider(
height: 0.1,
thickness: 4,
color: Colors.black12,
),
],
),
);
}
Widget _buildBottomDrawerBody(BuildContext context) {
print(listApps.length);
return SizedBox(
width: double.infinity,
height: _bodyHeight,
child: listApps.isNotEmpty
? ListView.builder(
itemCount: listApps.length,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (BuildContext context, int i) => Column(
children: [
Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
width: .1,
),
),
),
child: ListTile(
leading: Image.memory(listApps[i].icon),
title: Text(listApps[i].title),
subtitle: Text(listApps[i].package),
trailing: const Icon(Icons.timer),
visualDensity: const VisualDensity(vertical: -1),
),
)
],
),
)
: ListView.builder(
itemCount: 10,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (BuildContext context, int i) => Column(
children: [
Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
width: .1,
),
),
),
child: const ListTile(
leading: Icon(Icons.app_registration),
title: Text("listApps[i].title"),
subtitle: Text("listApps[i].package"),
trailing: Icon(Icons.timer),
visualDensity: VisualDensity(vertical: -1),
),
)
],
),
));
}
}
class AppModel {
final String title;
final String package;
final Uint8List icon;
AppModel({required this.title, required this.package, required this.icon});
}
experts can help with my problem :)..
if you found the solution, please explain the problem with our approach of refactoring.
In the extracted widget a set state is missing
void _getApp() async {
List<AppInfo> apps = await InstalledApps.getInstalledApps(true, true);
for (var app in apps) {
var item = AppModel(
title: app.name!,
package: app.getVersionInfo(),
icon: app.icon!,
);
listApps.add(item);
}
setState((){}}; //<--- here
}

how to send Data to other screen?

import 'package:flutter/material.dart';
import 'package:flutter_bmi_app/second_screen.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class BmiCalc extends StatefulWidget {
const BmiCalc({Key? key}) : super(key: key);
#override
State<BmiCalc> createState() => _BmiCalcState();
}
class _BmiCalcState extends State<BmiCalc> {
Color colorOfLittleBox = Color.fromARGB(255, 27, 28, 48);
Color colorOfLittleBox2 = Colors.pink;
bool isMale = true;
double _value = 150;
int weight = 60;
int age = 25;
double answer = 10;
String calc = "CALCULATE";
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 12, 9, 34),
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
Row(
children: [
FemaleBox("MALE", Icons.male),
FemaleBox("FEMALE", Icons.female),
],
),
Column(children: [
Container(
padding: EdgeInsets.all(32),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color.fromARGB(255, 27, 28, 48),
borderRadius: BorderRadius.circular(15),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("HEIGHT",
style: TextStyle(color: Colors.grey, fontSize: 20)),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_value.toStringAsFixed(0),
style: const TextStyle(
fontSize: 45,
color: Colors.white,
fontWeight: FontWeight.w900)),
const Text(
"cm",
style:
TextStyle(fontSize: 20, color: Colors.grey),
),
],
),
Slider(
min: 100,
max: 230,
thumbColor: Colors.pink,
value: _value,
onChanged: (value) {
setState(() {
_value = value;
});
},
),
],
))
]),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Operation("Weight"),
Operation("Age"),
],
),
Container(
decoration: BoxDecoration(
color: Colors.pink,
borderRadius: BorderRadius.circular(15),
),
padding: EdgeInsets.only(bottom: 5),
width: MediaQuery.of(context).size.width,
child: TextButton(
child: Text(
calc,
style: const TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.w900),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
),
)
],
),
),
),
);
}
void calculate() {
answer = (weight / (_value * _value)) * 10000;
Text(answer.toString(),
style: const TextStyle(fontSize: 40, color: Colors.white));
if (calc == "CALCULATE") {
calc = answer.toStringAsFixed(1);
} else {
calc = "CALCULATE";
}
setState(() {});
}
}
I made bmi calculator, I wanna have answer on other screen. I want to send this function calculate() to the second screen, where I will
have the answer of this calculation. I gave Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()), but how to make it work? Thank you in advance.
Make the SecondScreen constructor take a parameter for the type of data that you want to send to it.
const SecondScreen(
{Key? key,required this.answer, })
: super(key: key);
final String? answer; //define value you want to pass
#override
_SecondScreenScreenState createState() => _SecondScreenState();
}
And pass data when navigate
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(answer: 'Hello',),
));
here is the example:
import 'package:flutter/material.dart';
class Todo {
final String title;
final String description;
const Todo(this.title, this.description);
}
void main() {
runApp(
MaterialApp(
title: 'Passing Data',
home: TodosScreen(
todos: List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
),
);
}
class TodosScreen extends StatelessWidget {
const TodosScreen({Key? key, required this.todos}) : super(key: key);
final List<Todo> todos;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todos'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
// When a user taps the ListTile, navigate to the DetailScreen.
// Notice that you're not only creating a DetailScreen, you're
// also passing the current todo through to it.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
},
);
},
),
);
}
}
class DetailScreen extends StatelessWidget {
// In the constructor, require a Todo.
const DetailScreen({Key? key, required this.todo}) : super(key: key);
// Declare a field that holds the Todo.
final Todo todo;
#override
Widget build(BuildContext context) {
// Use the Todo to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(todo.description),
),
);
}
}
I can't get the FemaleBox and Operation in your project so I can't run that try the above example or share you full code include second screen also
add a constructor in your second screen and pass it while calling second screen
const SecondScreen(
{Key? key,required this.answer, })
: super(key: key);
final String? answer; //define value you want to pass
#override
_SecondScreenScreenState createState() => _SecondScreenState();
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(answer: 'Hello',),
));
There is another way to do that.
Create a class as given below and use static keyword to define any variable.
Now you can call this variable at your entire app via- Common.sharedData
So you can modified it according to you
Class Common{
static int sharedData=0;
//Other function
}
There are different ways to solve this
Sending parameters through constructor (Good solution).
Use a State Management package and hold the state in its class and access tit everywhere (Recommended way).
declare variable globally and use it anywhere in the app (not Recommended)

How can I send data from bottom sheet to parent widget?

I have list of products in bottom sheet, when I choose any product I want to parent's widget to add it, unfortunately my product adds only after hot reload, or when I create a new route from bottom sheet to parent's widget, how can I solve this problem, any ideas? Here is the part of the bottom sheet code
class IceBottomSheet extends StatefulWidget {
const IceBottomSheet({Key? key}) : super(key: key);
#override
_IceBottomSheetState createState() => _IceBottomSheetState();
}
class _IceBottomSheetState extends State<IceBottomSheet> {
final _model = ProductWidgetsModel();
#override
Widget build(BuildContext context) {
List<Widget> productWidgetList = [];
products.forEach((product) =>
productWidgetList.add(SingleProductWidget(product: product)));
return Provider(
model: _model,
child: Expanded(
child: GridView.count(
crossAxisSpacing: 10,
mainAxisSpacing: 16,
shrinkWrap: true,
crossAxisCount: 2, children: productWidgetList),
),
);
}
}
class SingleProductWidget extends StatefulWidget {
final Product product;
const SingleProductWidget({Key? key, required this.product})
: super(key: key);
#override
State<SingleProductWidget> createState() => _SingleProductWidgetState();
}
class _SingleProductWidgetState extends State<SingleProductWidget> {
#override
Widget build(BuildContext context) {
final model = Provider.of(context)?.model;
return Padding(
padding: const EdgeInsets.all(5.0),
child:
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
width: double.infinity,
height: 100,
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: widget.product.image,
),
shape: BoxShape.circle,
border: Border.all(
color: model?.idSelected == widget.product.id
? Colors.yellow
: Colors.grey,
width: 5.0,
style: BorderStyle.solid,
),
),
child: GestureDetector(
onTap: () {
model?.idSelected = widget.product.id;
// Route route =
// MaterialPageRoute(builder: (context) => BerryPage(context,));
// Navigator.push(context, route);
if(model?.idSelected == 1){
menuRow.removeAt(2);
Navigator.pop(context);
choice.insert(2, Adds(id: 102, name: 'Холодок', img: 'https://autogear.ru/misc/i/gallery/73434/2759438.jpg'));
}
}),
),
),
And here is the part of parent's widget code, it is inside GestureDetector
else if (index == 2){
setState(() {
});
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext builder) {
return Container(
height: 250,
child: Column(
children: [
SizedBox(
height: 10,
),Row(
children: [
Padding(
padding: EdgeInsets.only(left: MediaQuery.of(context).size.width*0.25 + MediaQuery.of(context).size.width *0.12),
child: Text(
'Холодок',
style: TextStyle(
fontFamily: 'Newfont',
fontSize: 22,
),
),
),
SizedBox(width: MediaQuery.of(context).size.width*0.25,),
IconButton(icon: Icon(Icons.close),onPressed: (){Navigator.pop(context);},)
],
),
Divider(),
IceBottomSheet(),
],
));
},
);
So when you open the BottomSheet you have to add await before it, so when you call Navigator.pop(context, data_you_want_to_pass_to_parent) it will wait for some data to be returned.
final data = await openBottomSheet();
inside the bottomSheet when you want to close, just pass the the desired data as so
Navigator.pop(context, data_you_want_to_pass_to_parent);