Add: images slider

This commit is contained in:
2024-05-31 14:55:24 +03:00
parent 3b593ad733
commit 1eeff4209e
4 changed files with 146 additions and 74 deletions

View File

@@ -10,6 +10,7 @@
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:enableOnBackInvokedCallback="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user

View File

@@ -1,5 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math';
import 'package:carousel_slider/carousel_slider.dart';
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/heading.dart'; import 'package:gymlink_module_web/components/heading.dart';
@@ -27,6 +29,8 @@ class _DetailPageState extends State<DetailPage> {
bool isInCart = false; bool isInCart = false;
int quantity = 0; int quantity = 0;
GymItem? item; GymItem? item;
final CarouselController _carouselController = CarouselController();
int _currentImage = 0;
@override @override
void initState() { void initState() {
@@ -53,6 +57,11 @@ class _DetailPageState extends State<DetailPage> {
setState(() { setState(() {
item = GymItem.fromJson(jsonDecode(utf8.decode(response.bodyBytes))); item = GymItem.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
}); });
WidgetsBinding.instance.addPostFrameCallback((_) {
for (var element in item!.images) {
precacheImage(NetworkImage(element.url), context);
}
});
} }
} }
@@ -173,9 +182,64 @@ class _DetailPageState extends State<DetailPage> {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image( item!.images.length > 1
image: NetworkImage(item!.images[0].url), ? Column(children: [
), CarouselSlider.builder(
itemCount: item!.images.length,
itemBuilder: (context, index, realIdx) {
return Center(
child: Image.network(
item!.images[realIdx].url,
width: min(
800,
MediaQuery.sizeOf(context)
.width)),
);
},
carouselController: _carouselController,
options: CarouselOptions(
enlargeCenterPage: true,
aspectRatio: 1,
enableInfiniteScroll: false,
onPageChanged: (index, reason) {
setState(() {
_currentImage = index;
});
}),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: item!.images
.asMap()
.entries
.map((entry) {
return GestureDetector(
onTap: () => _carouselController
.animateToPage(entry.key),
child: Container(
width: 12.0,
height: 12.0,
margin: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 4.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (Theme.of(context)
.brightness ==
Brightness.dark
? Colors.white
: Colors.black)
.withOpacity(
_currentImage == entry.key
? 0.9
: 0.4)),
),
);
}).toList(),
),
])
: Image.network(item!.images[0].url,
width: min(
800, MediaQuery.sizeOf(context).width)),
item!.description != '' item!.description != ''
? Padding( ? Padding(
padding: const EdgeInsetsDirectional.all(30), padding: const EdgeInsetsDirectional.all(30),

View File

@@ -95,14 +95,16 @@ class _MainPageState extends State<MainPage> {
} }
void _onLoad() async { void _onLoad() async {
setState(() { if (itemViewCount < filteredData.length) {
isLoading = true; setState(() {
}); isLoading = true;
await Future.delayed(const Duration(seconds: 1)); });
setState(() { await Future.delayed(const Duration(seconds: 1));
itemViewCount = min(filteredData.length, itemViewCount + 5); setState(() {
isLoading = false; itemViewCount = min(filteredData.length, itemViewCount + 5);
}); isLoading = false;
});
}
} }
void _onSearch() { void _onSearch() {
@@ -201,72 +203,76 @@ class _MainPageState extends State<MainPage> {
child: LazyLoadScrollView( child: LazyLoadScrollView(
onEndOfPage: _onLoad, onEndOfPage: _onLoad,
isLoading: isLoading, isLoading: isLoading,
child: ListView( child: Scrollbar(
children: [ child: ListView(
items.isEmpty children: [
? const Center(child: CircularProgressIndicator()) items.isEmpty
: filteredData.isEmpty ? const Center(child: CircularProgressIndicator())
? const Center(child: Text('Ничего не найдено')) : filteredData.isEmpty
: GridView.builder( ? const Center(child: Text('Ничего не найдено'))
physics: const AlwaysScrollableScrollPhysics(), : GridView.builder(
shrinkWrap: true, physics: const NeverScrollableScrollPhysics(),
gridDelegate: shrinkWrap: true,
SliverGridDelegateWithFixedCrossAxisCount( gridDelegate:
crossAxisCount: min( SliverGridDelegateWithFixedCrossAxisCount(
(MediaQuery.sizeOf(context).width ~/ 200) crossAxisCount: min(
.toInt(), (MediaQuery.sizeOf(context).width ~/ 200)
8), .toInt(),
), 8),
itemCount: itemViewCount, ),
itemBuilder: (context, index) { itemCount: itemViewCount,
final product = filteredData[index]; itemBuilder: (context, index) {
return ProductCard( final product = filteredData[index];
imagePath: Image( return ProductCard(
image: Image.network(product.images[0].url) imagePath: Image(
.image, image:
width: 50, Image.network(product.images[0].url)
), .image,
name: product.title, width: 50,
price: product.price.toString(), ),
onTap: () => Navigator.of(context).push( name: product.title,
CustomPageRoute( price: product.price.toString(),
builder: (context) => DetailPage( onTap: () => Navigator.of(context).push(
id: product.id, CustomPageRoute(
builder: (context) => DetailPage(
id: product.id,
),
), ),
), ),
), );
); },
}, ),
), itemViewCount > 0
itemViewCount > 0 ? Padding(
? Padding( padding: const EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.symmetric(vertical: 10), child: Center(
child: Center( child: itemViewCount < filteredData.length
child: itemViewCount < filteredData.length ? !isLoading
? !isLoading ? ElevatedButton(
? ElevatedButton( onPressed: _onLoad,
onPressed: _onLoad, style: ElevatedButton.styleFrom(
style: ElevatedButton.styleFrom( backgroundColor:
backgroundColor: Theme.of(context).primaryColor,
Theme.of(context).primaryColor, shape: const RoundedRectangleBorder(
shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(
borderRadius: BorderRadius.all( Radius.circular(50)),
Radius.circular(50)), ),
foregroundColor: Colors.white,
), ),
foregroundColor: Colors.white, child: const Text('Загрузить ещё'),
), )
child: const Text('Загрузить ещё'), : const CircularProgressIndicator()
) : const Text(
: const CircularProgressIndicator() 'Конец списка',
: const Text( style: TextStyle(
'Конец списка', fontSize: 10,
style: TextStyle( color: Color(0x88000000)),
fontSize: 10, color: Color(0x88000000)), ),
), ),
), )
) : const SizedBox.shrink(),
: const SizedBox.shrink(), ],
], ),
), ),
), ),
), ),

View File

@@ -40,6 +40,7 @@ dependencies:
lazy_load_scrollview: ^1.3.0 lazy_load_scrollview: ^1.3.0
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
flutter_svg: ^2.0.10+1 flutter_svg: ^2.0.10+1
carousel_slider: ^4.2.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: