수평으로 스크롤할 수 있으며 스냅 효과가 있는 카드(스냅 효과 포함)
왼쪽 또는 오른쪽에서 스와이프할 때 스냅과 함께 수평으로 스크롤하는 카드 목록을 만들고 싶습니다.
각 카드 사이에는 약간의 간격이 있으며 아래 이미지와 유사한 화면에 맞춥니다
그 외에도 이러한 수평 스크롤 가능한 목록 요소는 수직 스크롤 가능한 목록 안에 포함되어야 한다.
내가 할 수 있는 모든 것은 플러터 문서에서 예시를 따른 후 수평 스크롤 카드 목록을 표시하는 것이다.
class SnapCarousel extends StatelessWidget {
@override
Widget build(BuildContext context) {
final title = 'Horizontal List';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
height: 200.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
width: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
color: Colors.green,
),
Container(
width: 160.0,
color: Colors.yellow,
),
Container(
width: 160.0,
color: Colors.orange,
),
],
),
),
),
);
}
}
및 사용:
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: MyHomePage()));
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Carousel in vertical scrollable'),
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 16.0),
itemBuilder: (BuildContext context, int index) {
if(index % 2 == 0) {
return _buildCarousel(context, index ~/ 2);
}
else {
return Divider();
}
},
),
);
}
Widget _buildCarousel(BuildContext context, int carouselIndex) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Carousel $carouselIndex'),
SizedBox(
// you may want to use an aspect ratio here for tablet support
height: 200.0,
child: PageView.builder(
// store this controller in a State to save the carousel scroll position
controller: PageController(viewportFraction: 0.8),
itemBuilder: (BuildContext context, int itemIndex) {
return _buildCarouselItem(context, carouselIndex, itemIndex);
},
),
)
],
);
}
Widget _buildCarouselItem(BuildContext context, int carouselIndex, int itemIndex) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0),
child: Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
),
);
}
}
이것은 오래된 질문이며, 저는 다른 것을 찾으러 여기에 왔습니다 ;-). 하지만 이 패키지를 사용하면 WiVault의 외관상으로는 쉽게 해결할 수 있습니다:
구현:
종속성을 pubsec.yaml에 넣습니다:
dependencies:
flutter_swiper: ^1.1.6
필요한 페이지에서 가져오기:
import 'package:flutter_swiper/flutter_swiper.dart';
레이아웃:
new Swiper(
itemBuilder: (BuildContext context, int index) {
return new Image.network(
"http://via.placeholder.com/288x188",
fit: BoxFit.fill,
);
},
itemCount: 10,
viewportFraction: 0.8,
scale: 0.9,
)
스크린샷:
타사 패키지를 사용하지 않으려는 경우 다음과 같이 시도할 수 있습니다:
class _HomePageState extends State<HomePage> {
int _index = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: SizedBox(
height: 200, // card height
child: PageView.builder(
itemCount: 10,
controller: PageController(viewportFraction: 0.7),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Center(
child: Text(
"Card ${i + 1}",
style: TextStyle(fontSize: 32),
),
),
),
);
},
),
),
),
);
}
}
ListView를 통해 스냅 효과를 얻으려면 물리학을 PageScrollPhysics로 설정하십시오
const List<Widget> children = [
ContainerCard(),
ContainerCard(),
ContainerCard(),
];
ListView.builder(
scrollDirection: Axis.horizontal,
physics: const PageScrollPhysics(), // this for snapping
itemCount: children.length,
itemBuilder: (_, index) => children[index],
)
고급 스냅 목록
동적 항목 크기, 구성 가능한 스냅 지점, 항목 시각화 및 필수 제어(예: ScrollToIndex, 애니메이션)와 같은 고급 용도를 찾고 있는 경우에는 훨씬 더 많은 기능과 함께 기본 기반을 사용해야 합니다.
SnappyListView(
itemCount: Colors.accents.length,
itemBuilder: (context, index) {
return Container(
height: 100,
color: Colors.accents.elementAt(index),
child: Text("Index: $index"),
),
);
서드파티 라이브러리를 사용하고 싶지 않은 사람에게는 더 좋고 간단하다고 생각합니다. . 그래서 처음 페이지가 로드될 때마다 첫 번째와 두 번째 카드에는 애니메이션이 없고 카드를 스와이프할 때 이전 카드와 현재 카드에만 스케일 애니메이션이 적용됩니다. 다음은 제가 구현한 것입니다:
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex = -1, previousIndex = 0;
double getAnimationValue(int currentIndex, int widgetIndex, int previousIndex,
{bool begin = true}) {
if (widgetIndex == currentIndex) {
return begin ? 0.9 : 1;
} else {
return begin ? 1 : 0.9;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 200, // card height
child: PageView.builder(
itemCount: 10,
controller: PageController(viewportFraction: 0.7),
onPageChanged: (int index) {
setState(() {
if (currentIndex != -1) {
previousIndex = currentIndex;
}
currentIndex = index;
});
},
itemBuilder: (_, widgetIndex) {
return (currentIndex != -1 &&
(previousIndex == widgetIndex ||
widgetIndex == currentIndex))
? TweenAnimationBuilder(
duration: const Duration(milliseconds: 400),
tween: Tween<double>(
begin: getAnimationValue(
currentIndex,
widgetIndex,
previousIndex,
),
end: getAnimationValue(
currentIndex,
widgetIndex,
previousIndex,
begin: false,
),
),
builder: (context, value, child) {
return Transform.scale(
scale: value,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Card${widgetIndex + 1}",
style: const TextStyle(fontSize: 30),
),
Text(
"$widgetIndex >> Widget Index << $widgetIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$currentIndex >> Current Index << $currentIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$previousIndex >> Previous Index << $previousIndex",
style: const TextStyle(fontSize: 22),
),
],
),
),
);
},
)
: Transform.scale(
// this is used when you want to disable animation when initialized the page
scale:
(widgetIndex == 0 && currentIndex == -1) ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Card${widgetIndex + 1}",
style: const TextStyle(fontSize: 30),
),
Text(
"$widgetIndex >> Widget Index << $widgetIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$currentIndex >> Init Index << $currentIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$previousIndex >> Previous Index << $previousIndex",
style: const TextStyle(fontSize: 22),
),
],
),
),
);
},
),
),
],
),
);
}
}
나는 이 애니메이션에 트윈애니메이션빌더를 사용했고 위젯을 하드코딩했다. 필요할 때마다 위젯에 메소드를 사용하거나 쉬운 애니메이션을 위한 패키지를 사용할 수 있습니다.
'개발하자' 카테고리의 다른 글
Python PHP 세션 직렬화 해제 (0) | 2023.02.20 |
---|---|
POST 요청을 수행하는 동안 플러터 인증서_VERIFIY_FAILED 오류를 해결하는 방법은 무엇입니까? (0) | 2023.02.19 |
유형 스크립트: 익스프레스 연장.자체 클래스가 있는 세션 인터페이스 (0) | 2023.02.18 |
플러터 - 요소를 제거할 때 UI가 올바르게 업데이트되지 않음 (1) | 2023.02.18 |
Kubernetes 입력에서 라우팅 경로 제거 (1) | 2023.02.17 |