I am creating a slider where I want to show json data in carousel slider the data will be coming from API ofcourse
What are the steps need for that can you guys guide me like have to create http method to first read data.
CarouselSlider(
options: CarouselOptions(
aspectRatio: 1.5,
viewportFraction: 0.95,
enlargeStrategy: CenterPageEnlargeStrategy.height,
enlargeCenterPage: true,
reverse: false,
enableInfiniteScroll: true,
autoPlay: true,
autoPlayAnimationDuration: const Duration(milliseconds: 900),
initialPage: initialPage,
onPageChanged: (index, reason) {
setState(() {
initialPage = index;
debugPrint('$initialPage');
});
}),
items: Category.categories
.map((category) => HeroCarouselCard(category: category))
.toList(),
),
This is something I was firstly doing Category.categories is another equatable class where I was storing static data.
[
{
"CarouselName": "Others",
"CarouselDescription": "A smartphone and tablet-based solution to register and report Factory Assembly Line Inspection information.",
"CarouselImage": "banner3.png"
},
{
"CarouselName": "Production",
"CarouselDescription": "Grow your business",
"CarouselImage": "banner1.jpg"
}
]
following is the json that I wanna use
If I get a complete example would be great for me dont't wanna use getx
The best and easy way is to create a future builder and in future pass the api call in my case I have used http request to fetch all the data.
Container(
margin: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
future: future,
builder: (context, snapshot) {
final items = snapshot.data;
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: RepaintBoundary(
child: HomeCardShimmerEffect()),
);
default:
if (snapshot.hasError) {
return Center(
child: Column(
children: [
const Text(
'Something went Wrong Please try again'),
const SizedBox(
height: 5,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const BottomNavBar()));
},
child: const Text('Try Again'))
],
));
} else {
return HomeCardWidget(items: items!);
}
}
}),
)
Step 2:
Is to use Carousel.Builder instead of Carousel Simple and pass it items builder
and else it will work same as the data you show in listview.builder or gridview.builder
Passing context and fetching data according to that.
Hopefully this will be helpfull.
Related
I'm trying to make a budget app where each budget has its own spending history. Each of those spending histories would have a variable called 'budgetName' which I can compile and total the amount of spending by using sqflite code as below.
return await db.rawQuery("select sum(budgetSpent) as total from spending where budgetName ='" + budgetTitle + "'");
and this works if I try to use a .then((value) {print(value);}) when calling the sqflite function and see the value of each budget's spendings in the debug console.
But the problem is that I need the 'budgetTitle' when calling the function so it can compare with the spending's 'budgetName' to get the total spending amount.
So what I have right now is I try to get the spending amount like below:
child: BudgetCard(
budgetName: budget.budgetName,
budgetSpent: '${Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budget.budgetName}',
maxBudget: currency.format(int.parse(budget.maxBudget)),
svgIcon: iconListBudgetCards[budget.iconValue],
color: colorSwatch[budget.colorValue],
percentage: 0.5),
),
But it only returns Instance of 'Future<dynamic>' because it needs the 'await' before getting the value. But I couldn't find another way of doing this because it needs the 'budgetTitle' to be passed on.
Any help, ideas, or suggestions are highly appreciated! thank you in advance.
Here is the database code:
String? budgetSpendingAmount;
getSpecificSpending(budgetTitle) async {
dynamic result =
await SpendingDatabaseHelper.instance.getSpendingAmount(budgetTitle);
String a = result.toString();
debugPrint('A: $a');
if (a == '[{total: null}]') {
a = currency.format(int.parse('000'.trim()));
budgetSpendingAmount = a;
print(budgetSpendingAmount);
} else {
String? b = a.replaceAll(RegExp(r'[{\}\[\]\-]+'), '');
String c = b.substring(b.indexOf(":") + 1);
budgetSpendingAmount = currency.format(int.parse(c.trim()));
}
notifyListeners();
}
Future getSpendingAmount(String budgetTitle) async {
Database db = await instance.database;
return await db.rawQuery("select sum(budgetSpent) as total from spending where ='" + budgetTitle + "'");
}
Here is the full code of where I call the function to get the spending amount data:
Widget build(BuildContext context) {
return FutureBuilder<List<Budget>>(
future: Provider.of<BudgetDatabaseHelper>(context).getBudgets(),
/// Displaying the data from the list
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center();
}
return snapshot.data!.isEmpty
? const Flexible(
child: Center(
child: Padding(
padding: EdgeInsets.only(bottom: 80.0),
child: Text(
'You don\'t have any budget',
style: kCaption,
),
)))
: Flexible(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final budget = snapshot.data![index];
return Dismissible(
key: UniqueKey(),
background: const Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.only(bottom: 12.0, right: 24),
child: Icon(
IconlyLight.delete,
color: cRed,
size: 24,
),
),
),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
snapshot.data!.removeAt(index);
Provider.of<BudgetDatabaseHelper>(context,
listen: false)
.removeMethod(budget.id!, budget.budgetName);
},
child: GestureDetector(
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
enableDrag: true,
isScrollControlled: true,
builder: (context) {
return DraggableScrollableSheet(
snap: true,
minChildSize: 0.43,
maxChildSize: 0.85,
initialChildSize: 0.43,
snapSizes: const [0.43, 0.85],
builder: (context, scrollController) {
return ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32)),
child: Container(
color: cWhite,
child: SingleChildScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(),
child: BudgetDetails(
id: budget.id!,
budgetName: budget.budgetName,
budgetSpent: 'budgetSpent',
colorValue:
colorSwatch[budget.colorValue],
maxBudget: currency.format(
int.parse(budget.maxBudget)),
svgIcon: iconListBudgetDetails[
budget.iconValue],
),
),
),
);
},
);
},
),
child: BudgetCard(
budgetName: budget.budgetName,
budgetSpent: '${Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budget.budgetName}',
maxBudget: currency.format(int.parse(budget.maxBudget)),
svgIcon: iconListBudgetCards[budget.iconValue],
color: colorSwatch[budget.colorValue],
percentage: 0.5),
),
);
},
),
);
},
);
}
Use provider in a widget tree is not a good idea. Make a statefullWidget
Make a getter in your SpendingDatabaseHelper like this
String? _budgetSpendingAmount;
String get budgetSpendingAmount=> _budgetSpendingAmount;
and initialize it like this _budgetSpendingAmount = currency.format(int.parse(c.trim()));
So using this getter you can access this value anywhere in widget tree
Future<void> _getSpecificSpending(String budgetName)async{
try{
await Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budgetName);
} catch(e){
print('error :$e');
}
}
and in your widget tree write something like this
child: FutureBuilder(
future : _getSpecificSpending(budget.budgetName)
builder: (ctx,snapshot){
var spendDataProv=Provider.of<SpendingDatabaseHelper>(context, listen: false);
return snapshot.connectionState==ConnectionState.waiting ?
CircularProgressIndicator() :
BudgetCard(
budgetName: budget.budgetName,
budgetSpent:spendDataProv.budgetSpendingAmount ,
maxBudget: currency.format(int.parse(budget.maxBudget)),
svgIcon: iconListBudgetCards[budget.iconValue],
color: colorSwatch[budget.colorValue],
percentage: 0.5)
},
)
Some idea's
Use a FutureBuilder inside your BudgetCard widget. You can then show a CircularProgressIndicator where the spent amount is going to be when you are still waiting on the future to finish.
Or
Use a Boolean flag (which you flip at the beginning of the future method and at the end) that indicates whether the future is finished. Flag false: show progressIndicator, flag true show the spent amount.
Or
When calling Provider.of<BudgetDatabaseHelper>(context).getBudgets() you can let the method getBudgets() also fill an array with the information you need later on. So, call Provider.of<SpendingDatabaseHelper>(context, listen: false).getSpecificSpending(budget.budgetName) inside the getBudgets() method for each budgetName you have.
I want to display a list of images and pdf files that I am getting from API in response by separating them into PFD and image.
I have successfully displayed an images list that I am getting from API in circleAvatar using image.network, but now I am not getting the idea of how do I display a pdf list so that when I tap one pdf it opens in a viewer.
below is the image of how I want to display it in the list -:
the first blue circle is empty because it has a pdf path and the next has an image.
code for displaying the image and pdf file -
GridView.count(
crossAxisCount: 3,
children: List.generate(document?.length, (ind) {
return
Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
radius: 80,
backgroundImage:
NetworkImage('${api.getHTTPUri('/${document[ind].documentPath}')}'),
),
),
}),
),
you can use following package from pub.dev : flutter_pdfview
PDFView(
filePath: path,
enableSwipe: true,
swipeHorizontal: true,
autoSpacing: false,
pageFling: false,
onRender: (_pages) {
setState(() {
pages = _pages;
isReady = true;
});
},
onError: (error) {
print(error.toString());
},
onPageError: (page, error) {
print('$page: ${error.toString()}');
},
onViewCreated: (PDFViewController pdfViewController) {
_controller.complete(pdfViewController);
},
onPageChanged: (int page, int total) {
print('page change: $page/$total');
},
),
package link.
for list,
ListView.builder(
itemCount: length, //pass the length of list
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
//code for opening pdf goes here
},
child: Container(
color: Colors.grey.shade200,
margin: EdgeInsets.symmetric(
horizontal: 4, vertical: 8),
child: Row(children:[
//code of circle avatar
Text("//name of pdf at this index")])
),
);
},
),
link of tutorial for listview.builder
body: Container(
child: Consumer(builder: (context, watch, child) {
var wallet = watch(walletBuilderProvider);
//print(wallet.allWalletItems[0].eventName);
return WalletList(wallets: wallet.allWalletItems);
}),
)
final walletBuilderProvider =
ChangeNotifierProvider.autoDispose<WalletModel>((ref) {
final walletData = ref.watch(dataProvider);
// Create an object by calling the constructor of WalletModel
// Since we now have memory allocated and an object created, we can now call functions which depend on the state of an object, a "method"
final walletModel = WalletModel();
walletModel.buildWallet(walletItems: walletData);
return walletModel;
});
What I do initially to refresh all the data before it loads is I just call
context.refresh(dataProvider);
context.refresh(walletBuilderProvider);
Here is the List that gets called to display the data.
class WalletList extends StatelessWidget {
final List<Wallet> wallets;
WalletList({required this.wallets});
#override
Widget build(BuildContext context) {
return Container(
child: wallets.isEmpty
? Container(
height: 150,
child: Center(
child: Text(
"List is empty",
style: TextStyle(fontSize: 18, color: Colors.white),
),
),
)
: getWalletListItems());
// return ListView(
// children: getWalletListItems(),
// );
}
ListView getWalletListItems() {
print(wallets.length);
print("afterwallets");
var walletList = wallets
.map((walletItem) => WalletListItem(wallet: walletItem))
.toList();
return ListView.builder(
itemCount: walletList.length,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
double scale = 1.0;
return Opacity(
opacity: scale,
child: Align(
heightFactor: 0.7,
alignment: Alignment.topCenter,
child: walletList[index]),
);
});
}
}
What I want to do in the end is use some form of RefreshIndictator to refresh both providers but when I have been attempting to implement that in either the Consumer or the WalletList I haven't been seeing any change at all.
First walletBuilderProvider watch dataProvider so you only need to refresh dataProvider, that will force a refresh on all providers that depend on it
Have you tried using RefreshIndicator Widget?
RefreshIndicator(
onRefresh: () async => context.refresh(dataProvider),
child: WalletList(wallets: wallet.allWalletItems),
);
I'm getting images from API and show them into grid view but the requirement is that I press long on any index of the image,a selected icon should be visible on that index image.
but the problem is that when I press long at any index, the selected icon is visible on all indexes.
ScreenShot:
to resolve this, I made model class, in which there are datatype
first is boolean variable(isSelected) for each index, another is for PhotoDetails which is fetching from API, but unable to handle it with FutureBuilder, because it rebuilds the build method when I performed setState and isSelected becomes false.
Code:
Model class:
class Photos{
PhotoDetail photoDetail;
bool isSelected;
Photos({this.photoDetail, this.isSelected});
}
FutureBuilder:
Expanded(
child: FutureBuilder<PhotoModel>(
future: _photoApi.getPhotosByUserList(
token: widget.tokenId,
contactId: widget.userContent.id,
),
builder:(BuildContext context, AsyncSnapshot<PhotoModel> snapshot){
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError){
return Center(child: new Text('Error: ${snapshot.error}'));
}
List<Photos> photos =[];
snapshot.data.content.forEach((element) {
photos.add(
Photos(
isSelected: false,
photoDetail: element
)
);
});
print("photos photos photos length:${photos.length}");
return photos.length>0?
sliverGridWidget(context,photos)
:Container(
alignment: Alignment.center,
child: Text("Empty"),
);
}
)
)
Images in grid view:
Widget sliverGridWidget(BuildContext context, List<Photos> listPhotoDetail){
return StaggeredGridView.countBuilder(
padding: const EdgeInsets.all(8.0),
crossAxisCount: 6,
itemCount: listPhotoDetail.length,
itemBuilder: (context, index){
return InkWell(
onLongPress: (){
setState(() {
enable = true;
print("iinnndexxxxxxx:$index");
// listPhotoDetail[index].isSelected = true;
});
},
child: Container(
alignment: Alignment.bottomRight,
decoration: BoxDecoration(
color:Colors.grey[100],
image: DecorationImage(
image: NetworkImage(listPhotoDetail[index].photoDetail.image.fileUrl),
fit: BoxFit.cover
)
),
child:enable?
Image.asset('assets/icons/selected.png')
:Container()
),
);
},
staggeredTileBuilder: (index)=> view ?StaggeredTile.count(6,6):StaggeredTile.count(2,2),
mainAxisSpacing: 8.0,
crossAxisSpacing:8.0,
);
}
To solve it try to use a specific key for every image
I want to add a checkbox for GirdView in Flutter. The data was fetched from API request include attribute selected default is false. When I click on the checkbox of each Item it will change value is True and update on UI and I use Mobx to observe these change actions. When I debugging the values were changed but UI didn't update, I really don't know the reason. I added 2 pictures for UI and Mobx model below.
API:
{
"name": "HuynhDuy Phuc",
"birthday": "None",
"phone": "N/A",
"isSelected": false
},
{
"name": "Doan Phuc",
"birthday": "None",
"phone": "N/A",
"isSelected": false
},
{
"name": "Phuc Vu",
"birthday": "None",
"phone": "N/A",
"isSelected": false
},
final _userApiPresenter = Provider.of<UserApiPresenter>(context);
_userApiPresenter.fetchUsersList();
Observer(
name: 'ListHomePage',
builder: (BuildContext context) {
return (_userApiPresenter.userAPI != null)
? AnimationLimiter(
child: GridView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(12),
addAutomaticKeepAlives: true,
//Determine the number of cells per row
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemCount:
_userApiPresenter.userAPI.users.length,
itemBuilder: (context, index) {
User user =
_userApiPresenter.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>[
UserItem(
name: user.name,
type: user.name,
phone: user.phone,
birthday: user.birthday,
isSelected: user.selected,
),
Align(
alignment: Alignment.topRight,
child: Checkbox(
value: user.selected,
onChanged: (_) {
if(user.selected){
_userApiPresenter.changeStatusCheckBox(index: index);
} else{
_userApiPresenter.changeStatusCheckBox(index: index);
}
},
),
),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(BuildContext context) =>
UserDetailPage(
index: index,
name: user.name,
),
),
);
},
),
),
),
);
},
),
)
: Center(
child: CircularProgressIndicator(),
);
},
)
UI
Mobx model
Observable information about users array and the user model itself is missing, but what you need to do(if already not) is:
Make the array of users observable as well - this way any addition, deletion, etc will results in update of the number of user boxes in the UI
Make property selected of User observable also - this way when certain user 'selected' state is effected, the UI will render the change
And something off topic:
You don't need #action attribute on getUser method, because this method is not updating any observable data
If this answer does not solve your problem, please provide implementation of userApi and User :)
You just missing one thing.
mobX does not update UI unless you tell it main variable changed..
To do so, just add the following line of code to your changeStatusCheckBox()
_userAPI = _userAPI;