Add: Lazy loading and refresh on pull down
This commit is contained in:
@@ -4,6 +4,8 @@ import 'package:gymlink_module_web/components/basket_item_card.dart';
|
|||||||
import 'package:gymlink_module_web/components/heading.dart';
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
import 'package:gymlink_module_web/pages/order_confirmation.dart';
|
import 'package:gymlink_module_web/pages/order_confirmation.dart';
|
||||||
import 'package:gymlink_module_web/tools/prefs.dart';
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/routes.dart';
|
||||||
|
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';
|
||||||
|
|
||||||
List<Map<String, dynamic>> cart = [
|
List<Map<String, dynamic>> cart = [
|
||||||
{
|
{
|
||||||
@@ -195,6 +197,12 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
return const SizedBox(height: 10);
|
return const SizedBox(height: 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onLoad() async {
|
||||||
|
await Future.delayed(const Duration(microseconds: 1000));
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -212,7 +220,7 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
style: Theme.of(context).textTheme.bodyLarge),
|
style: Theme.of(context).textTheme.bodyLarge),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context, true),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -232,24 +240,28 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
context: context,
|
context: context,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: LazyLoadScrollView(
|
||||||
itemCount: cartItems.length,
|
onEndOfPage: _onLoad,
|
||||||
itemBuilder: (context, index) {
|
child: ListView.builder(
|
||||||
final item = cartItems[index];
|
itemCount: cartItems.length,
|
||||||
return BasketItemCard(
|
itemBuilder: (context, index) {
|
||||||
name: item['name'],
|
final item = cartItems[index];
|
||||||
price: item['price'],
|
return BasketItemCard(
|
||||||
id: item['id'],
|
name: item['name'],
|
||||||
image: Image(
|
price: item['price'],
|
||||||
image: AssetImage('assets/${item['image']}'),
|
id: item['id'],
|
||||||
width: 50,
|
image: Image(
|
||||||
),
|
image: AssetImage('assets/${item['image']}'),
|
||||||
onTapPlus: () => addItem(item['id'].toString()),
|
width: 50,
|
||||||
onTapMinus: () =>
|
),
|
||||||
removeItem(item['id'].toString()),
|
onTapPlus: () => addItem(item['id'].toString()),
|
||||||
quantity: item['count'].toString(),
|
onTapMinus: () {
|
||||||
);
|
removeItem(item['id'].toString());
|
||||||
},
|
},
|
||||||
|
quantity: item['count'].toString(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildSpacer(),
|
_buildSpacer(),
|
||||||
@@ -263,7 +275,7 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => Navigator.of(context).push(
|
onPressed: () => Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
CustomPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
const OrderConfirmationPage(),
|
const OrderConfirmationPage(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gymlink_module_web/components/app_bar.dart';
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
import 'package:gymlink_module_web/components/item_card.dart';
|
import 'package:gymlink_module_web/components/item_card.dart';
|
||||||
import 'package:gymlink_module_web/pages/basket.dart';
|
import 'package:gymlink_module_web/pages/basket.dart';
|
||||||
import 'package:gymlink_module_web/pages/detail.dart';
|
import 'package:gymlink_module_web/pages/detail.dart';
|
||||||
import 'package:gymlink_module_web/pages/order_history.dart';
|
import 'package:gymlink_module_web/pages/order_history.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
import 'package:gymlink_module_web/tools/relative.dart';
|
import 'package:gymlink_module_web/tools/relative.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/routes.dart';
|
||||||
|
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
const List<Map<String, String>> testData = [
|
const List<Map<String, String>> testData = [
|
||||||
{
|
{
|
||||||
"name": "Протеин",
|
"name": "Протеин 2",
|
||||||
"image": "product.png",
|
"image": "product.png",
|
||||||
"price": "120",
|
"price": "120",
|
||||||
"details": "Test details",
|
"details": "Test details",
|
||||||
@@ -59,6 +64,25 @@ class MainPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MainPageState extends State<MainPage> {
|
class _MainPageState extends State<MainPage> {
|
||||||
|
String searchText = '';
|
||||||
|
List<Map<String, String>> filteredData = [];
|
||||||
|
int cartLength = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
filteredData = testData;
|
||||||
|
getCart().then((value) {
|
||||||
|
setState(() {
|
||||||
|
cartLength = value.length;
|
||||||
|
});
|
||||||
|
}).whenComplete(() {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _goToPage() async {
|
Future<void> _goToPage() async {
|
||||||
final Uri url = Uri.parse('https://google.com');
|
final Uri url = Uri.parse('https://google.com');
|
||||||
if (!await launchUrl(url, webOnlyWindowName: '_blank')) {
|
if (!await launchUrl(url, webOnlyWindowName: '_blank')) {
|
||||||
@@ -66,6 +90,19 @@ class _MainPageState extends State<MainPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onLoad() async {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 1000));
|
||||||
|
debugPrint('aye');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSearch() {
|
||||||
|
setState(() {
|
||||||
|
filteredData = testData
|
||||||
|
.where((element) => (element['name']!).contains(searchText))
|
||||||
|
.toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -82,15 +119,18 @@ class _MainPageState extends State<MainPage> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
|
onChanged: (value) => setState(() {
|
||||||
|
searchText = value;
|
||||||
|
}),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Search',
|
hintText: 'Поиск',
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
suffixIcon: Padding(
|
suffixIcon: Padding(
|
||||||
padding: const EdgeInsets.only(right: 8),
|
padding: const EdgeInsets.only(right: 8),
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: _goToPage,
|
onPressed: _onSearch,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 8,
|
vertical: 8,
|
||||||
@@ -113,35 +153,12 @@ class _MainPageState extends State<MainPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
getSpacer(context: context, flex: 2),
|
getSpacer(context: context, flex: 2),
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(MaterialPageRoute(
|
|
||||||
builder: (context) => const BasketPage(),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.all(0),
|
|
||||||
minimumSize: const Size(50, kMinInteractiveDimension),
|
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
|
||||||
shape: const CircleBorder(
|
|
||||||
side: BorderSide(
|
|
||||||
color: Colors.black,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.shopping_basket,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 8,
|
width: 8,
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).push(MaterialPageRoute(
|
Navigator.of(context).push(CustomPageRoute(
|
||||||
builder: (context) => const HistoryPage(),
|
builder: (context) => const HistoryPage(),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
@@ -169,41 +186,94 @@ class _MainPageState extends State<MainPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GridView.builder(
|
child: LazyLoadScrollView(
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
onEndOfPage: _onLoad,
|
||||||
crossAxisCount: (MediaQuery.sizeOf(context).width ~/ 150)
|
child: Stack(
|
||||||
.floor(), //TODO: Make it adaptive size
|
children: [
|
||||||
),
|
GridView.builder(
|
||||||
itemCount: testData.length,
|
gridDelegate:
|
||||||
itemBuilder: (context, index) {
|
SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
final product = testData[index];
|
crossAxisCount: min(
|
||||||
return ProductCard(
|
(MediaQuery.sizeOf(context).width ~/ 200)
|
||||||
imagePath: Image(
|
.toInt(),
|
||||||
image: AssetImage('assets/${product['image']!}'),
|
8),
|
||||||
width: 50,
|
|
||||||
),
|
|
||||||
name: product['name']!,
|
|
||||||
price: product['price']!,
|
|
||||||
onTap: () => Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => DetailPage(
|
|
||||||
name: product['name']!,
|
|
||||||
description: product['details']!,
|
|
||||||
price: product['price']!,
|
|
||||||
id: product['id']!,
|
|
||||||
image: Image(
|
|
||||||
image:
|
|
||||||
AssetImage('assets/${product['image']!}'),
|
|
||||||
width: 300),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
itemCount: filteredData.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final product = filteredData[index];
|
||||||
|
return ProductCard(
|
||||||
|
imagePath: Image(
|
||||||
|
image: Image.network(
|
||||||
|
'https://rus-sport.net/upload/iblock/311/topb85ez18pq0aavohpa5zipk2sbfxll.jpg')
|
||||||
|
.image,
|
||||||
|
width: 50,
|
||||||
|
),
|
||||||
|
name: product['name']!,
|
||||||
|
price: product['price']!,
|
||||||
|
onTap: () => Navigator.of(context).push(
|
||||||
|
CustomPageRoute(
|
||||||
|
builder: (context) => DetailPage(
|
||||||
|
name: product['name']!,
|
||||||
|
description: product['details']!,
|
||||||
|
price: product['price']!,
|
||||||
|
id: product['id']!,
|
||||||
|
image: Image(
|
||||||
|
image: AssetImage(
|
||||||
|
'assets/${product['image']!}'),
|
||||||
|
width: 300),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
floatingActionButton: SizedBox(
|
||||||
|
height: 80,
|
||||||
|
width: 80,
|
||||||
|
child: FittedBox(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
FloatingActionButton(
|
||||||
|
onPressed: () => Navigator.of(context).push(CustomPageRoute(
|
||||||
|
builder: (context) => const BasketPage(),
|
||||||
|
)),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
child: CircleAvatar(
|
||||||
|
radius: 25,
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
child: const Icon(Icons.shopping_cart_outlined)),
|
||||||
|
),
|
||||||
|
cartLength > 0
|
||||||
|
? Positioned(
|
||||||
|
right: -3,
|
||||||
|
bottom: 0,
|
||||||
|
child: Card(
|
||||||
|
color: Colors.red,
|
||||||
|
child: SizedBox(
|
||||||
|
width: 20,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
cartLength.toString(),
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ class _OrderConfirmationPageState extends State<OrderConfirmationPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: const GymLinkAppBar(),
|
appBar: const GymLinkAppBar(),
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
body: Column(
|
body: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -117,6 +118,17 @@ class _OrderConfirmationPageState extends State<OrderConfirmationPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Электронная почта',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@@ -129,7 +141,7 @@ class _OrderConfirmationPageState extends State<OrderConfirmationPage> {
|
|||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
print('debugprint');
|
debugPrint('debugprint');
|
||||||
// if (kIsWeb) {
|
// if (kIsWeb) {
|
||||||
// Navigator.of(context).push(
|
// Navigator.of(context).push(
|
||||||
// MaterialPageRoute(
|
// MaterialPageRoute(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:gymlink_module_web/components/app_bar.dart';
|
|||||||
import 'package:gymlink_module_web/components/heading.dart';
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
import 'package:gymlink_module_web/components/history_item_card.dart';
|
import 'package:gymlink_module_web/components/history_item_card.dart';
|
||||||
import 'package:gymlink_module_web/tools/relative.dart';
|
import 'package:gymlink_module_web/tools/relative.dart';
|
||||||
|
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';
|
||||||
|
|
||||||
List<Map<String, String>> orders = [
|
List<Map<String, String>> orders = [
|
||||||
{"image": "product.png", "price": "120", "id": "66", "date": "11.09.2001"},
|
{"image": "product.png", "price": "120", "id": "66", "date": "11.09.2001"},
|
||||||
@@ -32,11 +33,42 @@ List<Map<String, String>> orders = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
class HistoryPage extends StatelessWidget {
|
class HistoryPage extends StatefulWidget {
|
||||||
const HistoryPage({
|
const HistoryPage({
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HistoryPage> createState() => _HistoryPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HistoryPageState extends State<HistoryPage> {
|
||||||
|
List<Map<String, String>> my_orders = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
my_orders = orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onLoad() async {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 1000));
|
||||||
|
setState(() {
|
||||||
|
my_orders.add(
|
||||||
|
{
|
||||||
|
"image": "product.png",
|
||||||
|
"price": "120",
|
||||||
|
"id": "666666",
|
||||||
|
"date": "11.09.2001"
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onRefresh() async {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 1000));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -49,21 +81,33 @@ class HistoryPage extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: LazyLoadScrollView(
|
||||||
itemCount: orders.length,
|
onEndOfPage: _onLoad,
|
||||||
itemBuilder: (context, index) {
|
scrollOffset: 200,
|
||||||
final item = orders[index];
|
child: RefreshIndicator(
|
||||||
return HistoryItemCard(
|
edgeOffset: 55,
|
||||||
id: item['id']!,
|
onRefresh: _onRefresh,
|
||||||
cost: item['price']!,
|
child: Stack(
|
||||||
date: item['date']!,
|
children: [
|
||||||
image: Image(
|
ListView.builder(
|
||||||
image: AssetImage('assets/${item['image']!}'),
|
itemCount: my_orders.length,
|
||||||
width: 50,
|
itemBuilder: (context, index) {
|
||||||
),
|
final item = my_orders[index];
|
||||||
status: OrderStatus.completed,
|
return HistoryItemCard(
|
||||||
);
|
id: item['id']!,
|
||||||
},
|
cost: item['price']!,
|
||||||
|
date: item['date']!,
|
||||||
|
image: Image(
|
||||||
|
image: AssetImage('assets/${item['image']!}'),
|
||||||
|
width: 50,
|
||||||
|
),
|
||||||
|
status: OrderStatus.completed,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
getSpacer(context: context)
|
getSpacer(context: context)
|
||||||
|
|||||||
8
lib/tools/routes.dart
Normal file
8
lib/tools/routes.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CustomPageRoute extends MaterialPageRoute {
|
||||||
|
CustomPageRoute({builder}) : super(builder: builder);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Duration get transitionDuration => const Duration(milliseconds: 0);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user