import 'package:flutter/cupertino.
dart';
import 'categories_model.dart';
class ProductsModel with ChangeNotifier {
int? id;
String? title;
int? price;
String? description;
CategoriesModel? category;
List<String>? images;
ProductsModel(
{this.id,
this.title,
this.price,
this.description,
this.category,
this.images});
ProductsModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
price = json['price'];
description = json['description'];
category = json['category'] != null
? CategoriesModel.fromJson(json['category'])
: null;
images = json['images'].cast<String>();
}
static List<ProductsModel> productsFromSnapshot(List productSnaphot) {
// print("data ${productSnaphot[0]}");
return productSnaphot.map((data) {
return ProductsModel.fromJson(data);
}).toList();
}
// ignore: constant_identifier_names
const String BASE_URL = "api.escuelajs.co";
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:store_api_flutter_course/consts/api_consts.dart';
import 'package:store_api_flutter_course/models/categories_model.dart';
import 'package:store_api_flutter_course/models/products_model.dart';
import 'package:store_api_flutter_course/models/users_model.dart';
class APIHandler {
static Future<List<dynamic>> getData(
{required String target, String? limit}) async {
try {
var uri = Uri.https(
BASE_URL,
"api/v1/$target",
target == "products"
?{
"offset": "0",
"limit": limit,
}
: {});
var response = await http.get(uri);
// print("response ${jsonDecode(response.body)}");
var data = jsonDecode(response.body);
List tempList = [];
if (response.statusCode != 200) {
throw data["message"];
}
for (var v in data) {
tempList.add(v);
// print("V $v \n\n");
}
return tempList;
} catch (error) {
log("An error occured $error");
throw error.toString();
}
}
static Future<List<ProductsModel>> getAllProducts(
{required String limit}) async {
List temp = await getData(
target: "products",
limit: limit,
);
return ProductsModel.productsFromSnapshot(temp);
}
static Future<List<CategoriesModel>> getAllCategories() async {
List temp = await getData(target: "categories");
return CategoriesModel.categoriesFromSnapshot(temp);
}
static Future<List<UsersModel>> getAllUsers() async {
List temp = await getData(target: "users");
return UsersModel.usersFromSnapshot(temp);
}
static Future<ProductsModel> getProductById({required String id}) async {
try {
// ignore: unrelated_type_equality_checks
// id == 88;
var uri = Uri.https(
BASE_URL,
"api/v1/products/$id",
);
var response = await http.get(uri);
// print("response ${jsonDecode(response.body)}");
var data = jsonDecode(response.body);
if (response.statusCode != 200) {
throw data["message"];
}
return ProductsModel.fromJson(data);
} catch (error) {
log("an error occured while getting product info $error");
throw error.toString();
}
}
}
import 'package:card_swiper/card_swiper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
import 'package:page_transition/page_transition.dart';
import 'package:store_api_flutter_course/consts/global_colors.dart';
import 'package:store_api_flutter_course/screens/categories_screen.dart';
import 'package:store_api_flutter_course/screens/feeds_screen.dart';
import 'package:store_api_flutter_course/screens/users_screen.dart';
import 'package:store_api_flutter_course/services/api_handler.dart';
import '../models/products_model.dart';
import '../widgets/appbar_icons.dart';
import '../widgets/feeds_grid.dart';
import '../widgets/sale_widget.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
class _HomeScreenState extends State<HomeScreen> {
late TextEditingController _textEditingController;
// List<ProductsModel> productsList = [];
@override
void initState() {
_textEditingController = TextEditingController();
super.initState();
}
@override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
appBar: AppBar(
// elevation: 4,
title: const Text('Home'),
leading: AppBarIcons(
function: () {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.fade,
child: const CategoriesScreen(),
),
);
},
icon: IconlyBold.category,
),
actions: [
AppBarIcons(
function: () {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.fade,
child: const UsersScreen(),
),
);
},
icon: IconlyBold.user3,
),
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
const SizedBox(
height: 18,
),
TextField(
controller: _textEditingController,
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: "Search",
filled: true,
fillColor: Theme.of(context).cardColor,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(
color: Theme.of(context).cardColor,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(
width: 1,
color: Theme.of(context).colorScheme.secondary,
),
),
suffixIcon: Icon(
IconlyLight.search,
color: lightIconsColor,
)),
),
const SizedBox(
height: 18,
),
Expanded(
child: SingleChildScrollView(
child: Column(children: [
SizedBox(
height: size.height * 0.25,
child: Swiper(
itemCount: 3,
itemBuilder: (ctx, index) {
return const SaleWidget();
},
autoplay: true,
pagination: const SwiperPagination(
alignment: Alignment.bottomCenter,
builder: DotSwiperPaginationBuilder(
color: Colors.white,
activeColor: Colors.red)),
// control: const SwiperControl(),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const Text(
"Latest Products",
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18,
),
),
const Spacer(),
AppBarIcons(
function: () {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.fade,
child: const FeedsScreen()));
},
icon: IconlyBold.arrowRight2),
],
),
),
FutureBuilder<List<ProductsModel>>(
future: APIHandler.getAllProducts(limit: "5"),
builder: ((context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
Center(
child:
Text("An error occured ${snapshot.error}"),
);
} else if (snapshot.data == null) {
const Center(
child: Text("No products has been added yet"),
);
}
return FeedsGridWidget(
productsList: snapshot.data!);
}))
]),
),
)
],
),
)),
);
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/products_model.dart';
import 'feeds_widget.dart';
class FeedsGridWidget extends StatelessWidget {
const FeedsGridWidget({Key? key, required this.productsList})
: super(key: key);
final List<ProductsModel> productsList;
@override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 5,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 0.0,
mainAxisSpacing: 0.0,
childAspectRatio: 0.6),
itemBuilder: (ctx, index) {
return ChangeNotifierProvider.value(
value: productsList[index],
child: const FeedsWidget(),
);
});
}
}
import 'package:fancy_shimmer_image/fancy_shimmer_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
import 'package:page_transition/page_transition.dart';
import 'package:provider/provider.dart';
import 'package:store_api_flutter_course/models/products_model.dart';
import '../consts/global_colors.dart';
import '../screens/product_details.dart';
class FeedsWidget extends StatelessWidget {
const FeedsWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final productsModelProvider = Provider.of<ProductsModel>(context);
Size size = MediaQuery.of(context).size;
return Padding(
padding: const EdgeInsets.all(2.0),
child: Material(
borderRadius: BorderRadius.circular(8.0),
color: Theme.of(context).cardColor,
child: InkWell(
borderRadius: BorderRadius.circular(8.0),
onTap: () {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.fade,
child: ProductDetails(id: productsModelProvider.id.toString(),),
),
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: RichText(
text: TextSpan(
text: '\$',
style: const TextStyle(
color: Color.fromRGBO(33, 150, 243, 1)),
children: <TextSpan>[
TextSpan(
text: "${productsModelProvider.price}",
style: TextStyle(
color: lightTextColor,
fontWeight: FontWeight.w600)),
]),
),
),
const Icon(IconlyBold.heart),
],
),
),
const SizedBox(height: 10),
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: FancyShimmerImage(
height: size.height * 0.2,
width: double.infinity,
errorWidget: const Icon(
IconlyBold.danger,
color: Colors.red,
size: 28,
),
imageUrl: productsModelProvider.images![0],
boxFit: BoxFit.fill,
),
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
productsModelProvider.title.toString(),
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: const TextStyle(
fontSize: 17,
// fontFamily: 'Roboto',
fontWeight: FontWeight.w700,
),
),
),
SizedBox(
height: size.height * 0.01,
),
],
),
),
),
);
}