Add favorite button to list by provider to flutter app
شرح اضافة favorite button الى flutter app بواسطة ال Provider
شرح الموضوع على الفيديو:
عبارة عن تطبيق يحتوي على عدة قوائم تحتوي على عنوان و وصف قصير حول الموقع وصورة الموقع والرابط الخاص بها وعند الضغط على اي قائمة تنتقل الى موقع الخاص بها مضاف اليها ايقونة عند الضغط عليها يظهر لك Dialog ل اضافة زر المفضلة مع ايقونة بعداد لتحديد عدد القوائم , بحيث بستطاعتك اضافة اي قايمة الى صفحة المفضلة والاطلاع عليها في وقت اخر.
1- نقوم باضافة حزمة Provaider لملف pubspace.yaml
dependencies:
provider: ^5.0.0
2- انشاء مجلد من نوع Directory :
ونسمية ب assets واضافة الصور التي تريد استخدامها داخلة ثم الذهاب الى ملف pubspace.yaml وتحرير مستودع استدعاء الصور ثم كتابة مسار واسم كل صورة مع صيغتها
3- انشاء ملف ب اسم item_model ثم نقوم ب انشاء كلاس ب اسم ItemModel من نوع ChangeNotifier
ثم نضيف داخلة العناصر المطلوب عرضها ثم تعريفها
class ItemModel with ChangeNotifier {
final String title;
final String describtion;
final String url;
final String id;
final String image;
ItemModel({
this.id,
this.title,
this.describtion,
this.url,
this.image,
});
}
4- انشاء ملف ب اسم model_favorite ثم نقوم ب انشاء كلاس ب اسم FavoriteModel من نوع ChangeNotifier
ثم نضيف داخلة نفس العناصر السابقة مع اضافة عنصر جديد ب اسم itemsId ثم نقوم بتعريفعا
class FavoriteModel with ChangeNotifier {
final String title;
final String describtion;
final String url;
final String id;
final String itemsId;
final String image;
FavoriteModel(
{this.id,
@required this.itemsId,
this.title,
this.describtion,
this.url,
this.image});
}
5- انشاء ملف جديد ب اسم items_provider ثم ثم نقوم ب انشاء كلاس ب اسم ItemsProvider من نوع ChangeNotifier
class ItemsProvider with ChangeNotifier {}
ثم ننشى داخل الكلاس قايمة بنفس العناصر التي تم تعريفها في كلاس ItemModel مع اضافة البيانات الخاصة بها
List<ItemModel> _provideritems = [
ItemModel(
id: '',
title: '',
describtion: '',
image: '',
url: '',
),];
نقوم ب استدعاء كلاس model_items
قم بتكرار القايمة بالعدد الذي المطلوب ثم قم بتعبية البيانات الخاصة بك كما في المثال بالاسفل بشرط الا تتشابة قيم id يجب ان تاخذ كل id قيمة مختلفة
ItemModel(
id: '1-T1',
title: 'flutter',
describtion:
'Flutter SDK is Google UI toolkit for crafting beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.',
image: 'assets/flutter.png',
url: 'https://flutter.dev/',
),
ثم نكتب كود الحصول على قايمة ItemModel بواسطة get
List<ItemModel> get provideritems {
return [..._provideritems];
}
ثم نكتب كود العثور على عناصر قايمة ItemModel بواسطة itemId
ItemModel findById(String itemId) {
return _provideritems.firstWhere((element) => element.id == itemId);
}
6- انشاء ملف جديد ب اسم favorite_provider ثم نقوم ب انشاء كلاس ب اسم FavoriteProvider من نوع ChangeNotifier
class FavoriteProvider with ChangeNotifier {}
ثم نقوم بتعريف favoriteItems داخل Map
Map<String, FavoriteModel> _favoriteItems = {};
ثم ننشى داخل الكلاس كود الحصول على قايمة favoriteItems بواسطة get
Map<String, FavoriteModel> get getFavoriteItems {
return {..._favoriteItems};
}
ثم نقوم ب استدعاء كلاس model_favorite
ثم نظيف وظيفة void addItemToFavorite وتعريف العناصر الخاصة بها مع اضافة الاداة الشرطية if and elso
void addItemToFavorite(
String itemId,
String name,
String url,
String describtion,
String image,
) {
if (_favoriteItems.containsKey(itemId)) {
_favoriteItems.update(
itemId,
(exitingFavoriteItem) => FavoriteModel(
id: exitingFavoriteItem.id,
itemsId: exitingFavoriteItem.itemsId,
title: exitingFavoriteItem.title,
describtion: exitingFavoriteItem.describtion,
url: exitingFavoriteItem.url,
image: exitingFavoriteItem.image));
} else {
_favoriteItems.putIfAbsent(
itemId,
() => FavoriteModel(
id: DateTime.now().toString(),
itemsId: itemId,
title: name,
describtion: describtion,
url: url,
image: image));
}
notifyListeners();
}
ثم نظيف الكود المسول عن ازالة العنصر من قايمة المفضلة
void removeItem(String itemId) {
_favoriteItems.remove(itemId);
notifyListeners();
}
بعد ذلك نظيف الكود المسول عن ازالة كل العنصر من قايمة المفضلة
void clearItem() {
_favoriteItems.clear();
notifyListeners();
}
7- ننتقل لملف main.dart
ونقوم بلصق الكود الاتي
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Provider Demo',
theme: ThemeData.light(),
home: Home(),
);
}
}
ثم نضيف خاصية MultiProvider و نظيف الكلاسين التابعين لل Provider
[FavoriteProvider + ItemsProvider] بعمل Wrap with widget لل MaterialApp ولصق الكود التالي
MultiProvider(
providers: [
ChangeNotifierProvider<ItemsProvider>(
create: (context) => ItemsProvider(),
),
ChangeNotifierProvider<FavoriteProvider>(
create: (context) => FavoriteProvider(),
),
],
8- انشاء ملف جديد ب اسم home ثم ثم نقوم ب انشاء كلاس ب اسم Home من نوع StatefulWidget
class Home extends StatefulWidget {
// static const routeName = '/Home';
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
title: Text(
'Home',
style: TextStyle(color: Colors.white),
),
centerTitle: true,actions: [IconButton(),
icon: Icon(
Icons.notification_add_rounded,
color: Colors.white,
),
onPressed: () {},
),],
);
}
}
نقوم بتعريف واستدعى كلا من ال [ itemsProvider + provideritems ]
final itemsProvider = Provider.of<ItemsProvider>(context);
List<ItemModel> itemsList = itemsProvider.provideritems;
نضيف داخل الكلاس Consumer خاص ب FavoriteProvider في Badge لاكن قبل ذلك يجب ان نستدعي حزمة Badges
dependencies:
badges: ^2.0.1
وهذا كود ال Consumer
Consumer<FavoriteProvider>(
builder: (_, favorite, ch) => Badge(
badgeColor: Colors.white,
animationType: BadgeAnimationType.slide,
toAnimate: true,
position: BadgePosition.topEnd(top: 5, end: 7),
badgeContent: Text(
favorite.getFavoriteItems.length.toString(),
style: TextStyle(color: Colors.red),
),
child: IconButton(
icon: Icon(
Icons.favorite,
color: Colors.red,
),
// onPressed: () {
// Navigator.of(context).pushNamed(FavoriteScreen.routeName);
},
),
),
),
ثم ننشى ال body التابع للكلاس عن طريق اضافة GridView.coint والتي ستكون المسولة عن عرض عناصر ItemsProvider الموجودة في قايمة itemsList وذلك ب استدعاء قيمها عن طريق hangeNotifierProvider.value
body: GridView.count(
crossAxisCount: 1,
childAspectRatio: 1 / 0.6,
children: List.generate(itemsList.length, (index) {
return ChangeNotifierProvider.value(
value: itemsList[index],
);
}),
),
ثم نضيف child يحتوي على كلاس ب اسم HomeItems والذي سيحدد طريقة عرض كل عنصر
child: HomeItems(),
9- فتح ملف جديد ب اسم home_items ثم ننشى كلاس ب اسم HomeItems من نوع StatefulWidget
import 'package:flutter/material.dart';
class HomeItems extends StatefulWidget {
@override
_HomeItemsState createState() => _HomeItemsState();
}
class _HomeItemsState extends State<HomeItems> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 250,
height: 290,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.15,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(''
// itemElements.image,
),
fit: BoxFit.contain,
),
),
),
),
],
),
],
),
FlatButton(
onPressed: () {
// _launchinbrowser(
// (itemElements.url),
// );
},
child: Container(
padding: EdgeInsets.only(left: 5),
margin: EdgeInsets.only(left: 5, bottom: 2, right: 3),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 4,
),
Container(
alignment: Alignment.center,
child: Text(
'',
// itemElements.title,
style: TextStyle(
fontSize: 20,
color: Colors.red,
fontWeight: FontWeight.w800),
),
),
Text(
'',
// itemElements.describtion,
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.bold),
),
],
),
),
)
],
),
),
);
}
}
ثم نقوم بتعريف ال ItemModel
final itemElements = Provider.of<ItemModel>(context);
ثم نضيف العناصر ال ItemModel داخل الكلاس
نقوم ب استدعى حزمة Url_launcher في ملف pubspace.yaml من اجل فتح الروابط عند الضغط عليها
dependencies:
url_launcher: ^6.0.9
نضيف معرف الحزمة
Future<void> _launchinbrowser(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
ثم نقوم ب اضافة تصميم الايقونة الخاصة ب مربع الظهور او ما يسمى Dialog
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(18.0),
child: Icon(
Icons.more_horiz_sharp,
color: Colors.blue,
)),
),
],
),
اضافة كود اظهار القوايم الى مربع الظهور او ما يسمى Dialog عن طريق InkWell والذي سيظهر اي قايمة تم اختيارها عن طريق ال itemId الخاص بها
onTap: () async {
showDialog(
context: context,
builder: (BuildContext context) => FeedDialog(
itemId: itemElements.id,
),
);
},
10- نقوم بقتح ملف جديد ب اسم feeds_dialog ثم ننشى كلاس ب اسم FeedDialog من نوع StatelessWidget
وهو المسول عن عرض القوايم في مربع الظهور او مايسمى Dialog
import 'package:flutter/material.dart';
class FeedDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return
}
}
نقوم بتعريف المتغير itemId داخل الكلاس
final String itemId;
const FeedDialog({@required this.itemId});
ثم نقوم بتعريف واستدعى كلا من ال
[ FavoriteProvider+ itemId + ItemsProvider ]
final itemsData = Provider.of<ItemsProvider>(context, listen: false);
final favoriteProvider = Provider.of<FavoriteProvider>(context);
final itemElements = itemsData.findById(itemId);
نقوم بتصميم شكل مربع الظهور او مايسمى Dialog ثم نضيف العناصر الى التصميم
Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: 0.0,
backgroundColor: Colors.transparent,
child: SingleChildScrollView(
child: Column(children: [
Container(
constraints: BoxConstraints(
minHeight: 100,
maxHeight: MediaQuery.of(context).size.height * 0.2),
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
),
child: Image.asset(
'',
// itemElements.image,
fit: BoxFit.contain,
)),
Container(
padding: EdgeInsets.all(8),
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'',
// itemElements.describtion,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15),
),
],
),
),
Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Flexible(
child: dialogContent(
context,
2,
favoriteProvider.getFavoriteItems.containsKey(itemId)
? () {}
: () {
favoriteProvider.addItemToFavorite(
itemId,
itemElements.describtion,
itemElements.url,
itemElements.title,
itemElements.image);
Navigator.canPop(context)
? Navigator.pop(context)
// ignore: unnecessary_statements
: null;
},
),
),
]),
),
Container(
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 1.3),
shape: BoxShape.circle),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(30),
splashColor: Colors.grey,
onTap: () =>
Navigator.canPop(context) ? Navigator.pop(context) : null,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.close, size: 28, color: Colors.white),
),
),
),
),
]),
),
);
نقوم بانشاء Widget ب اسم dialogContent
Widget dialogContent(BuildContext context, int index, Function fct) {
return FittedBox(
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: fct,
splashColor: Colors.grey,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
padding: EdgeInsets.all(4),
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10.0,
offset: const Offset(0.0, 10.0),
),
],
),
),
SizedBox(
height: 20,
)
],
),
),
),
),
);
}
ثم نقوم بتعريف واستدعى FavoriteProvider داخل ال Widget
final favorite = Provider.of<FavoriteProvider>(context);
ثم نقوم بتصميم شكل ايقونة المفضلة
child: ClipOval(
child: SizedBox(
width: 50,
height: 50,
child: Icon(
favorite.getFavoriteItems.containsKey(itemId)
? Icons.favorite
: Icons.favorite_border,
color: Colors.red,
size: 25,
),
),
),
11- نقوم ب اضافة ملف ب اسم dialog_methods يحتوي على كلاس ب اسم DialogMethods
وهو المسول عن تصميم شاشة التحذير للموافقة او الغاء حذف عناصر المفضلة
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class DialogMethods {
Future<void> showDialogg(String name, String describtion, Function fct,
BuildContext context) async {
showDialog(
context: context,
builder: (BuildContext ctx) {
return AlertDialog(
title: Row(
children: [
Expanded(
child: Icon(
Icons.warning,
color: Colors.red,
size: 50,
)),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(name),
),
],
),
content: Text(describtion),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('cancel')),
TextButton(
onPressed: () {
fct();
Navigator.pop(context);
},
child: Text('yes'))
],
);
});
}
}
12- اضافة ملف ب اسم favorite_screen يحتوي على كلاس ب اسم FavoriteScreen من نوع StatefulWidget
وهو المسول عن كيفية ظهور شاشتين : الاولى شاشة المفضلة في حال تم اضافة عناصر للمفضلة او شاشة المفضلة في حال لم يتم اضافة اي عنصر للمفضلة الكود يحتوي على 2 Scaffold و 2 body ثم نكتب كود الحصول على العناصر لكل شاشة على حدة
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class FavoriteScreen extends StatefulWidget {
// static const routeName = '/FavoriteScreen';
@override
_FavoriteScreenState createState() => _FavoriteScreenState();
}
class _FavoriteScreenState extends State<FavoriteScreen> {
@override
Widget build(BuildContext context) {
return favoriteProvider.getFavoriteItems.isEmpty
? Scaffold(body: FavoriteEmptyList())
: Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text(
'Favorite (${favoriteProvider.getFavoriteItems.length})'),
actions: [
IconButton(
onPressed: () {
globalMethods.showDialogg(
' Remove Item',
'It will remove from favorite list',
() => favoriteProvider.clearItem(),
context);
// cartProvider.clearCart();
},
icon: Icon(Icons.delete_forever),
)
],
),
body: Container(
margin: EdgeInsets.only(bottom: 60),
child: ListView.builder(
itemCount: favoriteProvider.getFavoriteItems.length,
itemBuilder: (BuildContext ctx, int index) {
return ChangeNotifierProvider.value(
value: favoriteProvider.getFavoriteItems.values
.toList()[index],
child: FavoriteFullList(
itemId: favoriteProvider.getFavoriteItems.keys
.toList()[index],
),
);
}),
),
);
}
}
نقوم بتعريف كلاس ال DialogMethods
DialogMethods globalMethods = DialogMethods();
ثم نعرف FavoriteProvider
final favoriteProvider = Provider.of<FavoriteProvider>(context);
13- اضافة ملف ب اسم favorite_empty_list يحتوي على كلاس ب اسم FavoriteEmptyList من نوع StatelessWidget
وهو المسول عن تصميم شاشة المفضلة في حال لم يتم اضافة اي عنصر
import 'package:flutter/material.dart';
import '../home.dart';
class FavoriteEmptyList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
// margin: EdgeInsets.only(top: 80),
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.3,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.contain,
image: AssetImage('assets/empty-wishlist.png'),
),
),
),
Text(
'No favorite items',
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).textSelectionColor,
fontSize: 36,
fontWeight: FontWeight.w600),
),
SizedBox(
height: 10,
),
Text(
'Add your favorite items',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black, fontSize: 26, fontWeight: FontWeight.w600),
),
SizedBox(
height: 30,
),
Container(
width: MediaQuery.of(context).size.width * 0.9,
height: MediaQuery.of(context).size.height * 0.1,
child: RaisedButton(
// onPressed: () {
// Navigator.of(context).pushNamed(Home.routeName);
// },
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: Colors.red),
),
color: Colors.redAccent,
child: Text(
'Back to Home'.toUpperCase(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 26,
fontWeight: FontWeight.w600),
),
),
),
],
);
}
}
14- اضافة ملف ب اسم favorite_full_list يحتوي على كلاس ب اسم FavoriteFullList من نوع StatefulWidget
وهو المسول عن تصميم شاشة المفضلة في حال تم اضافة العناصر للمفضلة
import 'package:flutter/material.dart';
class FavoriteFullList extends StatefulWidget {
@override
_FavoriteFullListState createState() => _FavoriteFullListState();
}
class _FavoriteFullListState extends State<FavoriteFullList> {
@override
Widget build(BuildContext context) {
return Container(height: 135,
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomRight: const Radius.circular(16.0),
topRight: const Radius.circular(16.0),
),
color: Colors.white),);}}
نقوم باضافة كود تصميم شاشة المفضلة
child: Row(
children: [
Container(
width: 130.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(''
// favoriteElements.image
),
fit: BoxFit.contain,
),
),
),
Flexible(
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Column(
children: [
FlatButton(
onPressed: () {
// _launchinbrowser(
// (favoriteElements.url),
// );
},
child: Row(
children: [
Expanded(
child: Text('',
// (favoriteElements.describtion),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.blue,
),
),
),
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(32.0),
// splashColor: ,
onTap: () {
globalMethods.showDialogg(
'Remove item',
'It will remove from favorite list',
() => favoriteProvider
.removeItem(widget.itemId),
context);
//
},
child: Container(
height: 50,
width: 50,
child: Icon(
Icons.cancel,
color: Colors.red,
size: 22,
),
),
),
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text('',
// favoriteElements.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 15),
),
),
],
),
],
),
),
),
],
),
نقوم بتعريف كلاس ال DialogMethods
DialogMethods globalMethods = DialogMethods();
ثم نقوم بتعريف واستدعى كلا من
[ FavoriteProvider + FavoriteModel ]
final favoriteProvider = Provider.of<FavoriteProvider>(context);
final favoriteElements = Provider.of<FavoriteModel>(context);
نقوم بتعريف المتغير itemId داخل الكلاس
final String itemId;
const FavoriteFullList({this.itemId});
ثم نقوم ب اضافة العناصر داخل الكلاس
ثم نقوم باستدعى معرف حزمة url_launcher
Future<void> _launchinbrowser(String url) async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: false,
forceWebView: true,
enableJavaScript: true,
headers: <String, String>{'header_key': 'header_value'},
);
} else {
throw 'Could not launch $url';
}
}
routes: {
'/FavoriteScreen': (context) => FavoriteScreen(),
'/Home': (context) => Home(),
},
تعليقات
إرسال تعليق