في هذا المقال سوف نتعلم كيفية إنشاء تطبيق Wallpaper من الصفر باستخدام واجهة برمجة تطبيقات flutter وموقع pixels بشكل (مجاني) تعتبر تطبيقات wallpaper من اشهر التطبيقات تحميلا في اسواق التطبيقات لذا اذا كنت تفكر في انشاء تطبيق wallpaper لنظام الاندرويد و الايفون والربح منه عن طريق اضافة اعلانات ادموب لتطبيق فلاتر ف انت في المكان الصحيح...
- متطلبات إنشاء تطبيق wallpaper
- انشاء ملفات التطبيق
- استيراد ال package لملف pubspec.yaml
- جلب البيانات من API و تحويلها لصيغة json
- كتابة وشرح اكواد التطبيق
- افتح برنامج اندرويد استوديو
- اضغط على New flutter project .
- حدد تطبيق Flutter في النافذة التي تظهر بعد ذلك ثم اضغط Next .
- سمي تطبيقك wallpaperapp . قد تضطر أيضًا إلى تحديد المسار إلى SDK اذا لم يكن محدد .ارجو الالتزام بنفس اسم التطبيق اذا كنت مبتدأ , وسوف اشرح لكم كيفية تغيير اسم التطبيق في مقال اخر.
- أدخل اسم الحزمة com.yourname.wallpaperapp .
- اختر اللغة البرمجية لتطبيق [ kotlin - Swift ]فقط.
- اختر منصة التطبيق [ Android - IOS ] فقط , ثم اضغط Finish ,وانتظر جتى يقوم التطبيق بانهاء اعداد التطبيق.
- قم باعداد مكونات التطبيق.
- شغل التطبيق على محاكي الاندرويد او على هاتفك الخاص,للتاكيد ان كل شي جاهز.
- متطلبات إنشاء تطبيق wallpaper
ضروريات :
Flutter environment sdk >=2.7.0 <3.0.0
minSdkVersion 24
targetSdkVersion 30
add uses permission to AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
كذلك قم ب اضافة هذا السطر في ملف AndroidManifest.xml
android:requestLegacyExternalStorage="true"
يفضل :
Android studio last version
Flutter last version
Dart last version
- انشاء ملفات التطبيق
قم بانشاء الملفات المحدد عليها اللون الاورق في الصورة. ارجو الالتزام بنفس اسماء الملفات التي في الصورة من اجل ضمان عدم حدوث اي اخطاء.
انتقل الى ملف assets ثم قم ب اضافة الصور الى ملف carousel التي ستعرض في الشاشة الرئسية بشكل متحرك , قم بتحميل الصور التي ستضيفها من هــنـــا ثم الصقها في ملف ال carousel ثم انتقل الى ملف pubspec.yaml وقم بنسخ سطر السماح 👇 ب اضافة الصور ولصقة في مكانة المناسب.
assets:
- assets/carousel/
- استيراد ال package لملف pubspec.yaml
من ملف pubspec.yaml يجب تغيير environment sdk الى ">=2.7.0 <3.0.0" اذا كنت مبتدأ في برمجة فلاتر و دارت, ثم انسخ الحزم التالية والصقها في ملف الpubspec.yaml , كما هو موضح في الصورة
http: ^0.12.1
cached_network_image: ^2.0.0
image_gallery_saver: ^1.6.8
permission_handler: ^4.2.0+hotfix.3
esys_flutter_share: ^1.0.2
bubble_bottom_bar: ^1.2.0
random_string: ^2.0.1
google_fonts: ^1.1.0
dio: ^3.0.9
flutter_staggered_grid_view: ^0.3.2
carousel_slider: ^2.3.1
font_awesome_flutter: ^8.10.0
wallpaper_manager: ^1.0.10
- جلب البيانات من API و تحويلها لصيغة json
- جلب البيانات :
انتقل الى تطبيق pixel وقم بانشاء حساب و بتسجيل الدخول , في اعلاء الشاشة لليمن اضغط على السهم الموجة للاسفل ثم اضغط على Image & Video API ثم ااضغط Your API Key بعد ذلك قم بنسخ API key الخاص بك سيكون عبارة عن ارقام واحرف مشابهه لهذا السطر [ 563492ad6f917000010000010725324606a945808f1b206b9fafa680 ] قم بحفظة في ملف text سنحتاجه لاحقا , ثم انتقل الى قسم ال Documentation بجواره.
شرح عناصر API request
Authorization او الترخيص
مطلوب لواجهة برمجة تطبيقات Pexels. يمكن لأي شخص لديه حساب Pexels أن يطلب API Key ، والذي ستتلقاه على الفور.
Pagination او الترقيم
تقوم معظم طلبات Pexels API بإرجاع سجلات متعددة في وقت واحد. كل endpoints مرقمة ويمكنها إرجاع 80 طلبًا كحد أقصى في وقت واحد. يقبل كل طلب مقسم إلى صفحات نفس العناصر ويعيد نفس بيانات ترقيم الصفحات في الاستجابة. من امثلتها :
next_page
prev_page
The Photo Resource او مصدر الصور
مصدر الصورة هو نسخة بتنسيق JSON من صورة Pexels.
id = هو معرف الصورة من نوع integer اي يقبل الاعدد الصحيحة.
url = هو عنوان رابط URL الخاص بـ Pexels حيث توجد الصورة من نوع string اي يقبل النصوص.
photographer = اسم المصور الذي التقط الصورة من نوع string اي يقبل النصوص.
photographer_url = عنوان URL لملف تعريف Pexels الخاص بالمصور من نوع string اي يقبل النصوص.
photographer_id = معرف المصور من نوع integer اي يقبل الاعدد الصحيحة.
src = مجموعة متنوعة من أحجام الصور المختلفة التي يمكن استخدامها لعرض هذه الصورة من نوع object.
وينقسم ال src الى عدد احجام منها :
The image scaled proportionally so that it's new height is 350px = medium .
The image cropped to W 800px X H 1200px = portrait.
The image cropped to W 1200px X H 627px = landscape
The image resized to W 940px X H 650px DPR 1 = large.
وهناك المزيد من الاحجام والعناصر اذا اردت التعرف عليها قم بزيارة هذا الرابط documentation
2.تحويل البيانات الى صيفة json :
ساشرحه في القسم كتابة وشرح اكواد التطبيق بالاسفل:
- كتابة وشرح اكواد التطبيق
ستجد شرح كل كود في تعليق في الاعلاء محصور بين هذه العلامة /// /// ...... /// ///
انتقل الى تطبيق wallpaperapp الخاص بك ثم انتقل الى ملف main.dart قم بحذف جميع الاكواد السابقة ثم الصق هذا الكود.
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'view/splash.dart'; void main() async { runApp(MyApp()); } class MyApp extends StatefulWidget { /// This widget is the root of your application. @override _MyAppState createState() => _MyAppState(); /// /// /// to set up turn on/off of theme data /// /// /// static _MyAppState of(BuildContext context) => context.findAncestorStateOfType<_MyAppState>(); } class _MyAppState extends State<MyApp> { ThemeMode _themeMode = ThemeMode.system; @override Widget build(BuildContext context) { /// /// to set screen for up and down /// /// SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); return new MaterialApp( theme: ThemeData(), darkTheme: ThemeData.dark(), themeMode: _themeMode, debugShowCheckedModeBanner: false, home: SplashScreen()); } /// /// /// code of theme data function /// /// /// void changeTheme(ThemeMode themeMode) { setState(() { _themeMode = themeMode; }); } }
سنقوم ببناء شاشات واجهة التطبيق
انتقل الى ملف splash ثم الصق هذا الكود.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:wallpaperapp/nav_bar/nav_bar.dart'; class SplashScreen extends StatefulWidget { @override _SplashScreenState createState() => _SplashScreenState(); } class _SplashScreenState extends State<SplashScreen> { @override void initState() { super.initState(); /// /// to control time of splashscreen /// /// Timer( Duration(seconds: 1), /// /// code of navigator to navigation bar /// /// () => Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => NavBar()))); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white30, body: Container( color: Colors.white, child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ SizedBox( width: MediaQuery.of(context).size.width, ), /// /// ui of splash screen /// /// Container( color: Colors.white, height: 683, width: 450, child: Image.asset( 'assets/carousel/splash.jpg', fit: BoxFit.fill, ), ), ], ), ), ); } }
انتقل الى ملف home ثم الصق هذا الكود.
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'view/search_view.dart';
import 'widget/carosel__widget.dart';
import 'widget/home_widget.dart';
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
TextEditingController searchController = new TextEditingController();
@override
void initState() {
super.initState();
}
/// /// ui of home screen /// ///
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Container(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 25,
),
/// /// //// app name /// ///
Text(
'Wallpapers',
style: GoogleFonts.openSans(
color: Colors.pinkAccent,
fontWeight: FontWeight.w700,
fontSize: 24),
),
SizedBox(
height: 16,
),
Container(
height: 150,
child: CaroselSliderWidget(),
),
SizedBox(
height: 16,
),
/// /// /// ui and code text field search in home screen /// /// ///
Container(
height: 50,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(16),
),
padding: EdgeInsets.symmetric(horizontal: 8),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: searchController,
decoration: InputDecoration(
hintText: "Search ", border: InputBorder.none),
)),
InkWell(
/// /// code of navigation to search view screen /// ///
onTap: () {
if (searchController.text != "") {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchView(
search: searchController.text,
)));
}
},
child: Container(child: Icon(Icons.search)))
],
),
),
SizedBox(
height: 8,
),
Text(
'Popular',
style: GoogleFonts.openSans(
color: Colors.pinkAccent,
fontWeight: FontWeight.w600,
fontSize: 18),
),
SizedBox(
height: 4,
),
SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height * 0.6,
child: HomeWidget(
home: "popular",
),
),
),
SizedBox(
height: 24,
),
],
),
),
),
),
);
}
}
انتقل الى ملف nav_bar ثم الصق هذا الكود.
import 'package:bubble_bottom_bar/bubble_bottom_bar.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:wallpaperapp/widget/category_widget.dart'; import '../home.dart'; import 'category.dart'; import 'setting.dart'; import 'trending.dart'; class NavBar extends StatefulWidget { @override _NavBarState createState() => _NavBarState(); } class _NavBarState extends State<NavBar> { int currentIndex; @override void initState() { super.initState(); currentIndex = 0; } void changePage(int index) { setState(() { currentIndex = index; }); } /// /// name of screens that show in navigation bar for each tab /// /// var screens = [Home(), CategoryScreen(), Trending(), Setting()]; int selectedTab = 0; @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: BubbleBottomBar( backgroundColor: Colors.pinkAccent, opacity: .2, currentIndex: currentIndex, onTap: changePage, /// /// ui navigation bar of all screens [home,category,trends,setting] /// /// items: <BubbleBottomBarItem>[ /// /// ui fo screens home for icons , text and color /// /// BubbleBottomBarItem( backgroundColor: Colors.black45, icon: Icon( Icons.home, color: Colors.black, ), activeIcon: Icon( Icons.home, color: Colors.white60, ), title: Text( "Home", style: GoogleFonts.poppins(color: Colors.white), )), /// /// ui fo screens Categories for icons , text and color /// /// BubbleBottomBarItem( backgroundColor: Colors.black45, icon: Icon( Icons.category, color: Colors.black, ), activeIcon: Icon( Icons.category, color: Colors.white60, ), title: Text( "Categories", style: GoogleFonts.poppins(color: Colors.white), )), /// /// ui fo screens Trending for icons , text and color /// /// BubbleBottomBarItem( backgroundColor: Colors.black45, icon: Icon( Icons.trending_up, color: Colors.black, ), activeIcon: Icon( Icons.trending_up, color: Colors.white60, ), title: Text( "Trending", style: GoogleFonts.poppins(color: Colors.white), )), /// /// ui fo screens Setting for icons , text and color /// /// BubbleBottomBarItem( backgroundColor: Colors.black45, icon: Icon( Icons.settings, color: Colors.black, ), activeIcon: Icon( Icons.settings, color: Colors.white60, ), title: Text( "Setting", style: GoogleFonts.poppins(color: Colors.white), )), ], ), body: screens[currentIndex], ); } }
انتقل الى ملف category ثم الصق هذا الكود.
import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:wallpaperapp/data/data.dart'; import 'package:wallpaperapp/models/categorie_model.dart'; import 'package:wallpaperapp/models/photos_model.dart'; import 'package:wallpaperapp/widget/category_widget.dart'; class CategoryScreen extends StatefulWidget { @override _CategoryScreenState createState() => _CategoryScreenState(); } class _CategoryScreenState extends State<CategoryScreen> { List<CategoryModel> categories = new List(); List<PhotosModel> photos = new List(); ScrollController _scrollController = new ScrollController(); @override void initState() { categories = getCategories(); super.initState(); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {} }); } @override Widget build(BuildContext context) { /// ui of category screen /// return Scaffold( body: ListView( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 16, ), Text( 'Categories', style: GoogleFonts.openSans( color: Colors.pinkAccent, fontWeight: FontWeight.w700, fontSize: 24), ), SizedBox( height: 16, ), /// /// to control of grid view for show list of category images /// /// StaggeredGridView.countBuilder( shrinkWrap: true, scrollDirection: Axis.vertical, physics: ScrollPhysics(), crossAxisCount: 4, itemCount: categories.length, itemBuilder: (BuildContext context, int index) => StagCategory( imgUrls: categories[index].imgUrl, categoryName: categories[index].categoryName, ), staggeredTileBuilder: (int index) => new StaggeredTile.fit(2), mainAxisSpacing: 8, crossAxisSpacing: 8, ), wallpaperCategory(photos, context), SizedBox( height: 24, ), ], ), ) ], ), ); } } /// /// ui for all list category image in category screen for shape image , url , name , color and style and place /// /// class StagCategory extends StatelessWidget { final String imgUrls, categoryName; StagCategory({ @required this.imgUrls, @required this.categoryName, }); @override Widget build(BuildContext context) { return GestureDetector( /// code of navigator to CategoryWidget /// onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => CategoryWidget( category: categoryName, ))); }, child: Container( child: // margin: EdgeInsets.only(right: 8), Stack( children: <Widget>[ ClipRRect( borderRadius: BorderRadius.circular(16), /// kIsWeb is A constant that is true if the application was compiled to run on the web./// child: kIsWeb ? Image.network( imgUrls, color: Colors.pink[200], ) : CachedNetworkImage( imageUrl: imgUrls, placeholder: (context, url) => Container( color: Colors.pink[200], ), fit: BoxFit.cover)), kIsWeb ? Positioned( bottom: 5, left: 10, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( categoryName, style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 24, ), ), ], )) : Positioned( bottom: 5, left: 10, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( categoryName, style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 24, ), ), ], )), ], )), ); } }
انتقل الى ملف setting ثم الصق هذا الكود.
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import '../main.dart'; class Setting extends StatefulWidget { @override _SettingState createState() => _SettingState(); } class _SettingState extends State<Setting> { @override Widget build(BuildContext context) { /// /// ui of setting screen /// /// return Scaffold( body: Center( child: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( height: 32, ), Text( 'Setting', style: GoogleFonts.openSans( color: Colors.pinkAccent, fontWeight: FontWeight.w700, fontSize: 24), ), /// /// ui of button theme switch [ light and dark ] for icons and text /// /// SizedBox( height: 50, ), /// /// code of button to turn light mode /// /// TextButton.icon( onPressed: () => MyApp.of(context).changeTheme(ThemeMode.light), icon: Icon( Icons.highlight, size: 30, ), label: Text( 'LightTheme', style: GoogleFonts.openSans( decoration: TextDecoration.underline, height: 1.5, fontSize: 30, fontWeight: FontWeight.bold, ), ), ), SizedBox( height: 20, ), /// /// code of button to turn dark mode /// /// TextButton.icon( onPressed: () => MyApp.of(context).changeTheme(ThemeMode.dark), icon: Icon( Icons.lightbulb, size: 30, ), label: Text( 'DarkTheme', style: GoogleFonts.openSans( decoration: TextDecoration.underline, height: 1.5, fontSize: 30, fontWeight: FontWeight.bold, ), ), ), ], ), )), ); } }
انتقل الى ملف trending ثم الصق هذا الكود.
import 'package:flutter/material.dart'; import 'package:wallpaperapp/widget/tranding_widget.dart'; class Trending extends StatefulWidget { @override _TrendingState createState() => _TrendingState(); } class _TrendingState extends State<Trending> { @override Widget build(BuildContext context) { return Scaffold( /// /// to show image in trends screen according TrandingWidget ui /// /// body: TrandingWidget( tranding: "trending", ), ); } }
انتقل الى ملف carosel__widget ثم الصق هذا الكود.
import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:http/http.dart' as http; import 'package:wallpaperapp/data/data.dart'; import 'package:wallpaperapp/models/photos_model.dart'; import 'package:wallpaperapp/view/image_view.dart'; class CaroselSliderWidget extends StatelessWidget { @override Widget build(BuildContext context) { return ListView( padding: EdgeInsets.all(0), children: <Widget>[ /// /// to control of carousel slider place , shape and movement in home screen /// /// CarouselSlider( options: CarouselOptions( height: 150.0, enlargeCenterPage: true, autoPlay: true, aspectRatio: 16 / 9, autoPlayCurve: Curves.fastOutSlowIn, enableInfiniteScroll: true, autoPlayAnimationDuration: Duration(milliseconds: 1200), viewportFraction: 0.8, ), /// /// list path of image that show in carousel slider from assets/carousel file /// /// items: [ Content( img: 'assets/carousel/motivation.jpg', title: 'Motivation', tag: 'motivation'), Content( img: 'assets/carousel/fashion.jpg', title: 'Fashion', tag: 'fashion'), Content( img: 'assets/carousel/anime.jpg', title: 'Anime', tag: 'anime'), Content( img: 'assets/carousel/art.jpg', title: 'Street Art', tag: 'art'), Content( img: 'assets/carousel/roses.jpg', title: 'Roses', tag: 'roses'), Content( img: 'assets/carousel/concert.jpg', title: 'Concert', tag: 'concert'), ], ), ], ); } } /// /// ui of CarouselSlider content[ img, title, tag ] /// /// class Content extends StatelessWidget { String img, title, tag; Content({this.img, this.title, this.tag}); @override Widget build(BuildContext context) { return GestureDetector( /// /// code of navigation to images inside Carousel slider /// /// onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => CarouselGrid( carousel: tag, ))); }, child: Container( width: MediaQuery.of(context).size.width, margin: EdgeInsets.all(5.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), image: DecorationImage( image: AssetImage(img), fit: BoxFit.cover, ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Text( title, style: GoogleFonts.openSans( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18.0, ), ), ], ), ), ); } } class CarouselGrid extends StatefulWidget { final String carousel; CarouselGrid({ @required this.carousel, }); @override _CarouselGridState createState() => _CarouselGridState(); } class _CarouselGridState extends State<CarouselGrid> { List<PhotosModel> photos = new List(); @override void initState() { getCarouselWallpaper(); super.initState(); } /// /// code to get image after click carousel slider by it's key from pixel site /// /// getCarouselWallpaper() async { await http.get( "https://api.pexels.com/v1/search?query=${widget.carousel}&per_page=50&page=2", headers: {"Authorization": apiKEY}).then((value) { Map<String, dynamic> jsonData = jsonDecode(value.body); jsonData["photos"].forEach((element) { PhotosModel photosModel = new PhotosModel(); photosModel = PhotosModel.fromMap(element); photos.add(photosModel); }); setState(() {}); }); } @override Widget build(BuildContext context) { return Scaffold( body: SingleChildScrollView( child: wallpaperCarousel(photos, context), ), ); } } /// /// ui of wallpaperCarousel to show images in grid view inside Carousel slider /// /// Widget wallpaperCarousel(List<PhotosModel> listPhotos, BuildContext context) { return Container( padding: EdgeInsets.symmetric(horizontal: 16), /// /// to control with images in grid view /// /// child: GridView.count( crossAxisCount: 3, childAspectRatio: 0.6, physics: ClampingScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.all(4.0), mainAxisSpacing: 6.0, crossAxisSpacing: 6.0, children: listPhotos.map((PhotosModel photoModel) { return GridTile( child: GestureDetector( /// /// code of navigation to image view /// /// onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ImageView( imgPath: photoModel.src.portrait, ))); }, child: Hero( /// /// to determine the type of collection of image from it's tag /// /// tag: photoModel.src.portrait, child: Container( child: ClipRRect( borderRadius: BorderRadius.circular(16), child: kIsWeb ? Image.network( photoModel.src.portrait, color: Colors.pink[200], height: 50, width: 100, fit: BoxFit.cover, ) : CachedNetworkImage( imageUrl: photoModel.src.portrait, placeholder: (context, url) => Container( color: Colors.pink[200], ), fit: BoxFit.cover)), ), ), )); }).toList()), ); }
انتقل الى ملف category_widget ثم الصق هذا الكود.
import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:wallpaperapp/data/data.dart'; import 'package:wallpaperapp/models/photos_model.dart'; import 'package:wallpaperapp/view/image_view.dart'; class CategoryWidget extends StatefulWidget { final String category; CategoryWidget({@required this.category}); @override _CategoryWidgetState createState() => _CategoryWidgetState(); } class _CategoryWidgetState extends State<CategoryWidget> { List<PhotosModel> photos = new List(); /// /// code to get image inside category screen by it's key from pixel site /// /// getCategorieWallpaper() async { await http.get( "https://api.pexels.com/v1/search?query=${widget.category}&per_page=50&page=1", headers: {"Authorization": apiKEY}).then((value) { Map<String, dynamic> jsonData = jsonDecode(value.body); jsonData["photos"].forEach((element) { PhotosModel photosModel = new PhotosModel(); photosModel = PhotosModel.fromMap(element); photos.add(photosModel); }); setState(() {}); }); } @override void initState() { getCategorieWallpaper(); super.initState(); } /// ui of category widget to show image after click list category image /// @override Widget build(BuildContext context) { return Scaffold( body: SingleChildScrollView( child: wallpaperCategory(photos, context), ), ); } } /// ui of wallpaperCategory widget to show image in grid view after click list category image /// Widget wallpaperCategory(List<PhotosModel> listPhotos, BuildContext context) { return Container( padding: EdgeInsets.symmetric(horizontal: 16), child: GridView.count( crossAxisCount: 3, childAspectRatio: 0.6, physics: ClampingScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.all(4.0), mainAxisSpacing: 6.0, crossAxisSpacing: 6.0, children: listPhotos.map((PhotosModel photoModel) { return GridTile( child: GestureDetector( /// /// code of navigator to ImageView /// /// onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ImageView( imgPath: photoModel.src.portrait, ))); }, child: Hero( /// /// to determine the type of category image from it's url /// /// tag: photoModel.src.portrait, child: Container( child: ClipRRect( borderRadius: BorderRadius.circular(16), /// kIsWeb is A constant that is true if the application was compiled to run on the web./// child: kIsWeb ? Image.network( photoModel.src.portrait, color: Colors.pink[200], height: 50, width: 100, fit: BoxFit.cover, ) : CachedNetworkImage( imageUrl: photoModel.src.portrait, placeholder: (context, url) => Container( color: Colors.pink[200], ), fit: BoxFit.cover)), ), ), )); }).toList()), ); }
انتقل الى ملف home_widget ثم الصق هذا الكود.
import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:wallpaperapp/data/data.dart'; import 'package:wallpaperapp/models/photos_model.dart'; import 'package:wallpaperapp/view/image_view.dart'; class HomeWidget extends StatefulWidget { final String home; HomeWidget({@required this.home}); @override _HomeWidgetState createState() => _HomeWidgetState(); } class _HomeWidgetState extends State<HomeWidget> { List<PhotosModel> photos = new List(); /// /// code to get image for Trends screen by it's key from pixel site /// /// getHomeWallpaper() async { await http.get("https://api.pexels.com/v1/curated?per_page=50&page=1", headers: {"Authorization": apiKEY}).then((value) { Map<String, dynamic> jsonData = jsonDecode(value.body); jsonData["photos"].forEach((element) { PhotosModel photosModel = new PhotosModel(); photosModel = PhotosModel.fromMap(element); photos.add(photosModel); }); setState(() {}); }); } @override void initState() { getHomeWallpaper(); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( body: SingleChildScrollView( child: wallpaperHome(photos, context), ), ); } } /// ui of wallpaperHome widget to show image in grid view in home screen /// Widget wallpaperHome(List<PhotosModel> listPhotos, BuildContext context) { return Container( padding: EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 5, ), SizedBox( height: 5, ), /// /// to control with images in grid view /// /// GridView.count( crossAxisCount: 3, childAspectRatio: 0.6, physics: ClampingScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.all(4.0), mainAxisSpacing: 6.0, crossAxisSpacing: 6.0, children: listPhotos.map((PhotosModel photoModel) { return GridTile( child: GestureDetector( /// /// code of navigation to image view /// /// onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ImageView( imgPath: photoModel.src.portrait, ))); }, child: Hero( tag: photoModel.src.portrait, child: Container( child: ClipRRect( borderRadius: BorderRadius.circular(16), child: kIsWeb ? Image.network( photoModel.src.portrait, color: Colors.pink[200], height: 50, width: 100, fit: BoxFit.cover, ) : CachedNetworkImage( imageUrl: photoModel.src.portrait, placeholder: (context, url) => Container( color: Colors.pink[200], ), fit: BoxFit.cover)), ), ), )); }).toList()), ], ), ); }
انتقل الى ملف tranding_widget ثم الصق هذا الكود.
import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:wallpaperapp/data/data.dart'; import 'package:wallpaperapp/models/photos_model.dart'; import 'package:wallpaperapp/view/image_view.dart'; class TrandingWidget extends StatefulWidget { final String tranding; TrandingWidget({ @required this.tranding, }); @override _TrandingWidgetState createState() => _TrandingWidgetState(); } class _TrandingWidgetState extends State<TrandingWidget> { List<PhotosModel> photos = new List(); /// /// code to get image for Trends screen by it's key from pixel site /// /// getTrendingWallpaper() async { await http.get("https://api.pexels.com/v1/curated?per_page=50&page=10", headers: {"Authorization": apiKEY}).then((value) { Map<String, dynamic> jsonData = jsonDecode(value.body); jsonData["photos"].forEach((element) { PhotosModel photosModel = new PhotosModel(); photosModel = PhotosModel.fromMap(element); photos.add(photosModel); }); setState(() {}); }); } @override void initState() { getTrendingWallpaper(); super.initState(); } /// /// ui of show image for trends screen /// /// @override Widget build(BuildContext context) { return Scaffold( body: SingleChildScrollView( child: wallpaperTranding(photos, context), ), ); } } /// /// ui of wallpaperTranding to show image in grid view to Trending screen /// /// Widget wallpaperTranding(List<PhotosModel> listPhotos, BuildContext context) { return Container( padding: EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 40, ), Text( 'Tranding', style: GoogleFonts.openSans( color: Colors.pinkAccent, fontWeight: FontWeight.w700, fontSize: 24), ), SizedBox( height: 20, ), /// /// to control with image inside grid view /// /// GridView.count( crossAxisCount: 3, childAspectRatio: 0.6, physics: ClampingScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.all(4.0), mainAxisSpacing: 6.0, crossAxisSpacing: 6.0, children: listPhotos.map((PhotosModel photoModel) { return GridTile( child: GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ImageView( imgPath: photoModel.src.portrait, ))); }, child: Hero( tag: photoModel.src.portrait, child: Container( child: ClipRRect( borderRadius: BorderRadius.circular(16), child: kIsWeb ? Image.network( photoModel.src.portrait, color: Colors.pink[200], height: 50, width: 100, fit: BoxFit.cover, ) : CachedNetworkImage( imageUrl: photoModel.src.portrait, placeholder: (context, url) => Container( color: Colors.pink[200], ), fit: BoxFit.cover)), ), ), )); }).toList()), ], ), ); }
انتقل الى ملف image_view ثم الصق هذا الكود.
import 'dart:io'; import 'dart:typed_data'; import 'dart:ui'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:wallpaper_manager/wallpaper_manager.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart'; class ImageView extends StatefulWidget { final String imgPath; ImageView({@required this.imgPath}); @override _ImageViewState createState() => _ImageViewState(); } class _ImageViewState extends State<ImageView> { var filePath; ByteData byteData; @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: <Widget>[ Hero( tag: widget.imgPath, child: Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: CachedNetworkImage( imageUrl: widget.imgPath, placeholder: (context, url) => Container( color: Colors.pink[200], ), fit: BoxFit.cover, ), ), ), /// /// code and ui of share icon to share image to others ////// ////// ////// Padding( padding: EdgeInsets.all(40), child: Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, alignment: Alignment.topRight, child: IconButton( onPressed: () async { var request = await HttpClient().getUrl(Uri.parse(widget.imgPath)); var response = await request.close(); Uint8List bytes = await consolidateHttpClientResponseBytes(response); await Share.file( 'Download Wallify Now!', 'amlog.jpg', bytes, 'image/jpg'); }, icon: Icon( FontAwesomeIcons.share, size: 34, color: Colors.white.withOpacity(0.7), ), ), ), ), /// /// ui of MaterialButton for apply and cancel /// /// Positioned( top: MediaQuery.of(context).size.height * 0.80, child: Container( width: MediaQuery.of(context).size.width, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ MaterialButton( height: MediaQuery.of(context).size.height * 0.05, minWidth: MediaQuery.of(context).size.width * 0.60, color: Colors.pinkAccent.withOpacity(0.7), onPressed: () { bottomSheet(context); }, shape: StadiumBorder(), child: Text( "Apply", style: TextStyle( color: Colors.white, fontWeight: FontWeight.w500, fontSize: 20), ), ), IconButton( onPressed: () { _save(context); }, icon: Icon( FontAwesomeIcons.arrowCircleDown, size: 34, color: Colors.white.withOpacity(0.7), ), ) ], ), ), ), Positioned( top: MediaQuery.of(context).size.height * 0.88, left: MediaQuery.of(context).size.width * 0.35, child: InkWell( onTap: () { Navigator.pop(context); }, child: MaterialButton( height: MediaQuery.of(context).size.height * 0.05, minWidth: MediaQuery.of(context).size.width * 0.30, color: Colors.pinkAccent.withOpacity(0.7), onPressed: () { Navigator.pop(context); }, shape: StadiumBorder(), child: Text( "Cancel", style: TextStyle( color: Colors.white, fontWeight: FontWeight.w500, fontSize: 20), ), ), ), ), ], ), ); } /// /// code of save image to your device /// /// _save(context) async { await _askPermission(); var response = await Dio().get(widget.imgPath, options: Options(responseType: ResponseType.bytes)); final result = await ImageGallerySaver.saveImage(Uint8List.fromList(response.data)); print(result); if (result != null) { var snackBar = SnackBar(content: Text('Wallpaper Downloaded')); ScaffoldMessenger.of(context).showSnackBar(snackBar); } Navigator.pop(context); } /// /// code of permission ask to allow use storage , camera and location when you click download and share image _askPermission() async { if (Platform.isIOS) { await PermissionHandler().requestPermissions([PermissionGroup.photos]); } else { PermissionHandler permission = PermissionHandler(); await permission.requestPermissions([ PermissionGroup.storage, PermissionGroup.camera, PermissionGroup.location ]); await permission.checkPermissionStatus(PermissionGroup.storage); } } /// /// ui of BottomSheet /// /// void bottomSheet(context) { showModalBottomSheet( shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0), )), context: context, builder: (BuildContext bc) { return Container( child: Wrap( children: <Widget>[ ListTile( leading: Icon(FontAwesomeIcons.lock), title: Text( 'Lock Screen', style: TextStyle( fontSize: 18, ), ), onTap: () { setWallpaperlock(context, widget.imgPath); }, ), ListTile( leading: Icon(FontAwesomeIcons.home), title: Text( 'Home Screen', style: TextStyle(fontSize: 18), ), onTap: () { setWallpaperhome(context, widget.imgPath); }), ListTile( leading: Icon(FontAwesomeIcons.android), title: Text( 'Both', style: TextStyle(fontSize: 18), ), onTap: () => {setWallpaperboth(context, widget.imgPath)}, ), ], ), ); }); } /// /// code of set wallpaper for luck screen /// /// void setWallpaperlock(BuildContext ctx, String image) async { try { var file = await DefaultCacheManager().getSingleFile(image); int location = WallpaperManager.LOCK_SCREEN; String result = await WallpaperManager.setWallpaperFromFile(file.path, location); if (result != null) { var snackBar = SnackBar(content: Text('Wallpaper set')); ScaffoldMessenger.of(context).showSnackBar(snackBar); Navigator.pop(context); } } catch (e) { print("exception e"); } } /// /// code of set wallpaper for home screen /// /// void setWallpaperhome(BuildContext ctx, String image) async { try { var file = await DefaultCacheManager().getSingleFile(image); int location = WallpaperManager.HOME_SCREEN; String result = await WallpaperManager.setWallpaperFromFile(file.path, location); if (result != null) { var snackBar = SnackBar(content: Text('Wallpaper set')); ScaffoldMessenger.of(context).showSnackBar(snackBar); Navigator.pop(context); } } catch (e) { print("exception e"); } } /// /// code of set wallpaper for both home screen and luck screen /// /// void setWallpaperboth(BuildContext ctx, String image) async { try { var file = await DefaultCacheManager().getSingleFile(image); int location = WallpaperManager.BOTH_SCREENS; String result = await WallpaperManager.setWallpaperFromFile(file.path, location); if (result != null) { var snackBar = SnackBar(content: Text('Wallpaper set')); ScaffoldMessenger.of(context).showSnackBar(snackBar); Navigator.pop(context); } } catch (e) { print("exception e"); } } }
انتقل الى ملف search_view ثم الصق هذا الكود.
import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:wallpaperapp/data/data.dart'; import 'package:wallpaperapp/models/photos_model.dart'; import 'image_view.dart'; class SearchView extends StatefulWidget { final String search; SearchView({@required this.search}); @override _SearchViewState createState() => _SearchViewState(); } class _SearchViewState extends State<SearchView> { List<PhotosModel> photos = new List(); TextEditingController searchController = new TextEditingController(); /// code and map json to get images in search text failed from pixel site /// /// getSearchWallpaper(String searchQuery) async { await http.get( "https://api.pexels.com/v1/search?query=$searchQuery&per_page=30&page=1", headers: {"Authorization": apiKEY}).then((value) { Map<String, dynamic> jsonData = jsonDecode(value.body); jsonData["photos"].forEach((element) { PhotosModel photosModel = new PhotosModel(); photosModel = PhotosModel.fromMap(element); photos.add(photosModel); }); setState(() {}); }); } @override void initState() { getSearchWallpaper(widget.search); searchController.text = widget.search; super.initState(); } @override Widget build(BuildContext context) { /// /// ui of search text failed in search screen /// /// return Scaffold( body: Stack( children: [ SingleChildScrollView( child: Container( child: Column( children: <Widget>[ SizedBox( height: 48, ), Container( decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(30), ), margin: EdgeInsets.symmetric(horizontal: 24), padding: EdgeInsets.symmetric(horizontal: 24), child: Row( children: <Widget>[ Expanded( child: TextField( controller: searchController, decoration: InputDecoration( hintText: "search", border: InputBorder.none), )), InkWell( onTap: () { getSearchWallpaper(searchController.text); }, child: Container(child: Icon(Icons.search))) ], ), ), SizedBox( height: 30, ), wallPaper(photos, context), ], ), ), ), ], )); } } /// /// ui for show result images in grid view in after searching in search page /// /// Widget wallPaper(List<PhotosModel> listPhotos, BuildContext context) { return Container( padding: EdgeInsets.symmetric(horizontal: 12), /// /// to control with image in grid view in search screen/// /// child: GridView.count( crossAxisCount: 3, childAspectRatio: 0.5, physics: ScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.all(4.0), mainAxisSpacing: 6.0, crossAxisSpacing: 6.0, children: listPhotos.map((PhotosModel photoModel) { return GridTile( child: GestureDetector( /// /// code of navigation to image view /// /// onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ImageView( imgPath: photoModel.src.portrait, ))); }, child: Hero( tag: photoModel.src.portrait, child: Container( child: ClipRRect( borderRadius: BorderRadius.circular(12), child: CachedNetworkImage( imageUrl: photoModel.src.portrait, placeholder: (context, url) => Container( color: Colors.pink[200], ), fit: BoxFit.cover)), ), ), )); }).toList()), ); }
انتقل الى ملف photos_model ثم الصق هذا الكود.
/// /// identified items Photo resource and json map for fetch data of image from site /// /// class PhotosModel { String url; String photographer; String photographerUrl; int photographerId; SrcModel src; /// photographer = The name of the photographer who took the photo. /// url = The Pexels URL where the photo is located. /// photographerUrl = The URL of the photographer's Pexels profile. /// photographerId = The id of the photographer. /// src = An assortment of different image sizes that can be used to display this Photo. PhotosModel( {this.url, this.photographer, this.photographerId, this.photographerUrl, this.src}); factory PhotosModel.fromMap(Map<String, dynamic> parsedJson) { return PhotosModel( url: parsedJson["url"], photographer: parsedJson["photographer"], photographerId: parsedJson["photographer_id"], photographerUrl: parsedJson["photographer_url"], src: SrcModel.fromMap(parsedJson["src"])); } } /// /// identified class items and json map for fetch data of shape and size image from site /// /// class SrcModel { String portrait; String large; String landscape; String medium; SrcModel({this.portrait, this.landscape, this.large, this.medium}); factory SrcModel.fromMap(Map<String, dynamic> srcJson) { return SrcModel( portrait: srcJson["portrait"], large: srcJson["large"], landscape: srcJson["landscape"], medium: srcJson["medium"]); } }
انتقل الى ملف categorie_model ثم الصق هذا الكود.
/// /// identified class of Category Model screen /// /// class CategoryModel { String categoryName; String imgUrl; }
انتقل الى ملف data ثم الصق هذا الكود.
import 'package:wallpaperapp/models/categorie_model.dart'; /// /// this is apikey from pixel site /// /// /// /// "copy your Access Key from unsplash site and paste it here" /// /// String apiKEY = "563492ad6f917000010000010725324606a945808f1b206b9fafa680"; /// /// list of category image Url that showed in category screen: Copied from pixel site /// List<CategoryModel> getCategories() { List<CategoryModel> categories = new List(); CategoryModel categoryModel = new CategoryModel(); /// 1 /// categoryModel.imgUrl = "https://images.pexels.com/photos/7790344/pexels-photo-7790344.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"; categoryModel.categoryName = "Happy Prithday"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 2 /// categoryModel.imgUrl = "https://images.pexels.com/photos/1851164/pexels-photo-1851164.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"; categoryModel.categoryName = "Animals"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 3 /// categoryModel.imgUrl = "https://images.pexels.com/photos/34950/pexels-photo.jpg?auto=compress&cs=tinysrgb&dpr=2&w=500"; categoryModel.categoryName = "Nature"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 4 /// categoryModel.imgUrl = "https://images.pexels.com/photos/3214982/pexels-photo-3214982.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Tourism"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 5 /// categoryModel.imgUrl = "https://images.pexels.com/photos/5442472/pexels-photo-5442472.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Jewelry"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 6 /// categoryModel.imgUrl = "https://images.pexels.com/photos/2943358/pexels-photo-2943358.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Bikes"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 7 /// categoryModel.imgUrl = "https://images.pexels.com/photos/3156482/pexels-photo-3156482.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Cars"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 8 /// categoryModel.imgUrl = "https://images.pexels.com/photos/776636/pexels-photo-776636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Love"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 9 /// categoryModel.imgUrl = "https://images.pexels.com/photos/5952647/pexels-photo-5952647.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Coding"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 10 /// categoryModel.imgUrl = "https://images.pexels.com/photos/2448749/pexels-photo-2448749.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500"; categoryModel.categoryName = "Raining"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 11 /// categoryModel.imgUrl = "https://images.pexels.com/photos/9392358/pexels-photo-9392358.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "History"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 12 /// categoryModel.imgUrl = "https://images.pexels.com/photos/39649/solar-flare-sun-eruption-energy-39649.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Space"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 13 /// categoryModel.imgUrl = "https://images.pexels.com/photos/1431282/pexels-photo-1431282.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; categoryModel.categoryName = "Sport"; categories.add(categoryModel); categoryModel = new CategoryModel(); /// 14 /// categoryModel.imgUrl = "https://images.pexels.com/photos/1109197/pexels-photo-1109197.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"; categoryModel.categoryName = "Food"; categories.add(categoryModel); categoryModel = new CategoryModel(); return categories; }
في حال واجهتك اي مشكلة لاتتردد في كتابة في تعليق ^_^
GooD LooK FoR All
تعليقات
إرسال تعليق