Add: images slider
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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(),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user