Add: Lazy loading and refresh on pull down

This commit is contained in:
2024-05-15 00:34:05 +03:00
parent baf85776e9
commit 464f51238f
5 changed files with 239 additions and 93 deletions

View File

@@ -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,6 +240,8 @@ class _BasketPageState extends State<BasketPage> {
context: context, context: context,
children: [ children: [
Expanded( Expanded(
child: LazyLoadScrollView(
onEndOfPage: _onLoad,
child: ListView.builder( child: ListView.builder(
itemCount: cartItems.length, itemCount: cartItems.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -245,13 +255,15 @@ class _BasketPageState extends State<BasketPage> {
width: 50, width: 50,
), ),
onTapPlus: () => addItem(item['id'].toString()), onTapPlus: () => addItem(item['id'].toString()),
onTapMinus: () => onTapMinus: () {
removeItem(item['id'].toString()), removeItem(item['id'].toString());
},
quantity: item['count'].toString(), quantity: item['count'].toString(),
); );
}, },
), ),
), ),
),
_buildSpacer(), _buildSpacer(),
Padding( Padding(
padding: const EdgeInsetsDirectional.symmetric( padding: const EdgeInsetsDirectional.symmetric(
@@ -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(),
), ),

View File

@@ -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,31 +186,40 @@ 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(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: min(
(MediaQuery.sizeOf(context).width ~/ 200)
.toInt(),
8),
), ),
itemCount: testData.length, itemCount: filteredData.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final product = testData[index]; final product = filteredData[index];
return ProductCard( return ProductCard(
imagePath: Image( imagePath: Image(
image: AssetImage('assets/${product['image']!}'), image: Image.network(
'https://rus-sport.net/upload/iblock/311/topb85ez18pq0aavohpa5zipk2sbfxll.jpg')
.image,
width: 50, width: 50,
), ),
name: product['name']!, name: product['name']!,
price: product['price']!, price: product['price']!,
onTap: () => Navigator.of(context).push( onTap: () => Navigator.of(context).push(
MaterialPageRoute( CustomPageRoute(
builder: (context) => DetailPage( builder: (context) => DetailPage(
name: product['name']!, name: product['name']!,
description: product['details']!, description: product['details']!,
price: product['price']!, price: product['price']!,
id: product['id']!, id: product['id']!,
image: Image( image: Image(
image: image: AssetImage(
AssetImage('assets/${product['image']!}'), 'assets/${product['image']!}'),
width: 300), width: 300),
), ),
), ),
@@ -201,9 +227,53 @@ class _MainPageState extends State<MainPage> {
); );
}, },
), ),
],
),
),
), ),
], ],
), ),
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,
); );
} }
} }

View File

@@ -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(

View File

@@ -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,10 +81,18 @@ class HistoryPage extends StatelessWidget {
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: ListView.builder( child: LazyLoadScrollView(
itemCount: orders.length, onEndOfPage: _onLoad,
scrollOffset: 200,
child: RefreshIndicator(
edgeOffset: 55,
onRefresh: _onRefresh,
child: Stack(
children: [
ListView.builder(
itemCount: my_orders.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = orders[index]; final item = my_orders[index];
return HistoryItemCard( return HistoryItemCard(
id: item['id']!, id: item['id']!,
cost: item['price']!, cost: item['price']!,
@@ -65,6 +105,10 @@ class HistoryPage extends StatelessWidget {
); );
}, },
), ),
],
),
),
),
), ),
getSpacer(context: context) getSpacer(context: context)
], ],

8
lib/tools/routes.dart Normal file
View 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);
}