├── .gitignore
├── .metadata
├── README.md
├── android.iml
├── assets
└── images
│ ├── img1.jpeg
│ ├── img2.jpeg
│ ├── img3.jpeg
│ ├── img4.jpeg
│ ├── img5.jpeg
│ └── img6.jpeg
├── carousel_cards.iml
├── lib
├── card_info.dart
├── carousel_body.dart
├── carousel_screen.dart
└── main.dart
├── pubspec.yaml
└── test
└── widget_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .atom/
3 | .idea
4 | .vscode/
5 | .packages
6 | .pub/
7 | build/
8 | ios/.generated/
9 | packages
10 | pubspec.lock
11 | .flutter-plugins
12 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 2e449f06f0a3be076e336ad6b30b0e9ec99dbdfe
8 | channel: alpha
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Overlapping Cards with Carousel Effect
2 |
3 | # Screencast
4 | 
5 |
--------------------------------------------------------------------------------
/android.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/assets/images/img1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoojaB26/CarouselOverlappingCards-Flutter/e1587bf9fc28622d94c93c414efc4871ead032ee/assets/images/img1.jpeg
--------------------------------------------------------------------------------
/assets/images/img2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoojaB26/CarouselOverlappingCards-Flutter/e1587bf9fc28622d94c93c414efc4871ead032ee/assets/images/img2.jpeg
--------------------------------------------------------------------------------
/assets/images/img3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoojaB26/CarouselOverlappingCards-Flutter/e1587bf9fc28622d94c93c414efc4871ead032ee/assets/images/img3.jpeg
--------------------------------------------------------------------------------
/assets/images/img4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoojaB26/CarouselOverlappingCards-Flutter/e1587bf9fc28622d94c93c414efc4871ead032ee/assets/images/img4.jpeg
--------------------------------------------------------------------------------
/assets/images/img5.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoojaB26/CarouselOverlappingCards-Flutter/e1587bf9fc28622d94c93c414efc4871ead032ee/assets/images/img5.jpeg
--------------------------------------------------------------------------------
/assets/images/img6.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoojaB26/CarouselOverlappingCards-Flutter/e1587bf9fc28622d94c93c414efc4871ead032ee/assets/images/img6.jpeg
--------------------------------------------------------------------------------
/carousel_cards.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/lib/card_info.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | final category = new Row(
4 | children: [
5 | Container(
6 | width: 3.0,
7 | height: 12.0,
8 | color: Colors.redAccent,
9 | ),
10 | SizedBox(width: 10.0,),
11 | new Text("International Offer",
12 | style: new TextStyle(fontSize: 11.0),
13 | )
14 | ],
15 | );
16 |
17 | final titleStyle = new TextStyle(
18 | color: Colors.black,
19 | fontSize: 18.0,
20 | fontWeight: FontWeight.w700
21 | );
22 | final title = new Container(
23 | padding: new EdgeInsets.only(left: 15.0),
24 | child: new Text("Introducing myBiz", style: titleStyle,),
25 | );
26 |
27 | final subTitleStyle = new TextStyle(
28 | color: Colors.black,
29 | fontSize: 14.0,
30 | fontWeight: FontWeight.w300
31 | );
32 | final subTitle = new Container(
33 | padding: new EdgeInsets.only(left: 15.0, right: 15.0),
34 | child: new Text("A smart corporate tool for business travelers. \n Flat 10,000 benefits on for First Booking", style: subTitleStyle,
35 | maxLines: 3,),
36 | );
--------------------------------------------------------------------------------
/lib/carousel_body.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:carousel_cards/card_info.dart';
3 |
4 | const double _mViewportFraction = 0.9;
5 |
6 | class CarouselBody extends StatefulWidget {
7 | CarouselBody({Key key}) : super(key: key);
8 |
9 | @override
10 | _CarouselBodyState createState() => _CarouselBodyState();
11 | }
12 |
13 | class _CarouselBodyState extends State {
14 | final PageController _backgroundController = PageController(viewportFraction: _mViewportFraction);
15 | final PageController _cardController =
16 | PageController(viewportFraction: _mViewportFraction);
17 | ValueNotifier selectedIndex = ValueNotifier(0.0);
18 |
19 | bool _handleNotification(ScrollNotification notification,
20 | PageController leader, PageController follower) {
21 | if (notification.depth == 0 && notification is ScrollUpdateNotification) {
22 | selectedIndex.value = leader.page;
23 | if (follower.page != leader.page) {
24 | //follower.animateToPage(leader.page.toInt(), duration: Duration(milliseconds: 10000), curve: Curves.easeOut);
25 | follower.position.jumpToWithoutSettling(leader.position.pixels);// ignore: deprecated_member_use
26 | //return true --> makes the card also take uneven space like the bg
27 | }
28 | setState(() {});
29 | }
30 | return false;
31 | }
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | return Stack(
36 | children: [
37 | /*PageView(
38 | controller: _backgroundController,
39 | children: _buildBackgroundImages(),
40 | ),*/
41 | NotificationListener(
42 | onNotification: (ScrollNotification notification) {
43 | return _handleNotification(
44 | notification, _cardController, _backgroundController);
45 | },
46 | child: PageView(
47 | controller: _backgroundController,
48 | children: _buildBackgroundImages(),
49 | ),
50 | ),
51 | NotificationListener(
52 | onNotification: (ScrollNotification notification) {
53 | return _handleNotification(
54 | notification, _cardController, _backgroundController);
55 | },
56 | child: PageView(
57 | controller: _cardController,
58 | children: _buildCards(),
59 | ),
60 | ),
61 | ],
62 | );
63 | }
64 |
65 | Iterable _buildBackgroundImages() {
66 | final List backgroundPages = [];
67 |
68 | double bgHeight = MediaQuery.of(context).size.height;
69 | double bgWidth = MediaQuery.of(context).size.width * 0.8;
70 |
71 |
72 | for (int index = 0; index < 6; index++) {
73 |
74 | var alignment = Alignment.center.add(
75 | Alignment((selectedIndex.value - index), 0.0));
76 | var resizeFactor =
77 | (1 - (((selectedIndex.value - index).abs() * 0.4).clamp(0.0, 1.0)));
78 |
79 | var imageAsset = 'assets/images/img${index + 1}.jpeg';
80 |
81 |
82 | backgroundPages.add(SizedBox(
83 | width: bgWidth ,
84 | height: bgHeight * resizeFactor,
85 | child: Container(
86 | margin: new EdgeInsets.only(bottom: 80.0),
87 | alignment: alignment,
88 | decoration: new BoxDecoration(
89 | image: new DecorationImage(
90 | image: new AssetImage(imageAsset),
91 | fit: BoxFit.cover,
92 | ),
93 | ),
94 | child: Opacity(
95 | opacity: 0.3,
96 |
97 | ),
98 | ),
99 | ));
100 | }
101 | return backgroundPages;
102 | }
103 |
104 | final roundedCard = new Container(
105 | // child: image,
106 | decoration: new BoxDecoration(
107 | color: Colors.white,
108 | shape: BoxShape.rectangle,
109 | borderRadius: new BorderRadius.circular(8.0),
110 | boxShadow: [
111 | new BoxShadow(
112 | color: Colors.black12,
113 | blurRadius: 10.0,
114 | offset: new Offset(0.0, 10.0),
115 | ),
116 | ],
117 | ),
118 |
119 | child: new Column(
120 | mainAxisSize: MainAxisSize.max,
121 | mainAxisAlignment: MainAxisAlignment.start,
122 | crossAxisAlignment: CrossAxisAlignment.start,
123 | children: [
124 | SizedBox(height: 10.0,),
125 | category,
126 | SizedBox(height: 25.0,),
127 | title,
128 | SizedBox(height: 15.0,),
129 | subTitle,
130 | ],
131 | ),
132 | );
133 |
134 | Iterable _buildCards() {
135 | final List pages = [];
136 | double bgHeight = MediaQuery.of(context).size.height * 0.3;
137 | double bgWidth = MediaQuery.of(context).size.width * 0.8; // changes width of the image
138 | for (int index = 0; index < 6; index++) {
139 | var alignment = Alignment.center.add(
140 | Alignment((selectedIndex.value - index) * _mViewportFraction, 0.0));
141 | var resizeFactor =
142 | (1 - (((selectedIndex.value - index).abs() * 0.0).clamp(0.0, 1.0)));
143 | pages.add(
144 | Container(
145 | alignment: alignment,
146 | child: new Container(
147 | margin: new EdgeInsets.only(top: 50.0),
148 | width: bgWidth * resizeFactor,
149 | height: bgHeight * resizeFactor,
150 | child: GestureDetector(
151 | onTap: () {
152 | Navigator.of(context).push(
153 | MaterialPageRoute(builder: (context) {
154 | return Hero(
155 | tag: index,
156 | child: null
157 |
158 | );
159 | }));
160 | },
161 | child: Hero(
162 | tag: index,
163 | child: roundedCard
164 | ),
165 | ),
166 | ),
167 | )
168 | );
169 | }
170 | return pages;
171 | }
172 | }
--------------------------------------------------------------------------------
/lib/carousel_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:carousel_cards/carousel_body.dart';
3 |
4 | class CarouselScreen extends StatelessWidget{
5 | @override
6 | Widget build(BuildContext context) {
7 | return new Scaffold(
8 |
9 | body: SafeArea(
10 | child: new Column(
11 | mainAxisSize: MainAxisSize.max,
12 | children: [
13 |
14 | new SizedBox(
15 | height: 250.0,
16 | child: new CarouselBody(),
17 | )
18 | ],
19 | ),
20 | ),
21 | );
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:carousel_cards/carousel_screen.dart';
3 |
4 | void main() => runApp(MyApp());
5 |
6 | class MyApp extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | return MaterialApp(
10 | debugShowCheckedModeBanner: false,
11 | home: CarouselScreen(),
12 | );
13 | }
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: carousel_cards
2 | description: Carousel Cards
3 |
4 | dependencies:
5 | flutter:
6 | sdk: flutter
7 |
8 | # The following adds the Cupertino Icons font to your application.
9 | # Use with the CupertinoIcons class for iOS style icons.
10 | cupertino_icons: ^0.1.0
11 |
12 | dev_dependencies:
13 | flutter_test:
14 | sdk: flutter
15 |
16 |
17 | # For information on the generic Dart part of this file, see the
18 | # following page: https://www.dartlang.org/tools/pub/pubspec
19 |
20 | # The following section is specific to Flutter.
21 | flutter:
22 |
23 | # The following line ensures that the Material Icons font is
24 | # included with your application, so that you can use the icons in
25 | # the material Icons class.
26 | uses-material-design: true
27 |
28 | # To add assets to your application, add an assets section, like this:
29 | assets:
30 | - assets/images/img1.jpeg
31 | - assets/images/img2.jpeg
32 | - assets/images/img3.jpeg
33 | - assets/images/img4.jpeg
34 | - assets/images/img5.jpeg
35 | - assets/images/img6.jpeg
36 |
37 |
38 | # An image asset can refer to one or more resolution-specific "variants", see
39 | # https://flutter.io/assets-and-images/#resolution-aware.
40 |
41 | # For details regarding adding assets from package dependencies, see
42 | # https://flutter.io/assets-and-images/#from-packages
43 |
44 | # To add custom fonts to your application, add a fonts section here,
45 | # in this "flutter" section. Each entry in this list should have a
46 | # "family" key with the font family name, and a "fonts" key with a
47 | # list giving the asset and other descriptors for the font. For
48 | # example:
49 | # fonts:
50 | # - family: Schyler
51 | # fonts:
52 | # - asset: fonts/Schyler-Regular.ttf
53 | # - asset: fonts/Schyler-Italic.ttf
54 | # style: italic
55 | # - family: Trajan Pro
56 | # fonts:
57 | # - asset: fonts/TrajanPro.ttf
58 | # - asset: fonts/TrajanPro_Bold.ttf
59 | # weight: 700
60 | #
61 | # For details regarding fonts from package dependencies,
62 | # see https://flutter.io/custom-fonts/#from-packages
63 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties
5 | // are correct.
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | import 'package:carousel_cards/main.dart';
11 |
12 | void main() {
13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
14 | // Build our app and trigger a frame.
15 | await tester.pumpWidget(new MyApp());
16 |
17 | // Verify that our counter starts at 0.
18 | expect(find.text('0'), findsOneWidget);
19 | expect(find.text('1'), findsNothing);
20 |
21 | // Tap the '+' icon and trigger a frame.
22 | await tester.tap(find.byIcon(Icons.add));
23 | await tester.pump();
24 |
25 | // Verify that our counter has incremented.
26 | expect(find.text('0'), findsNothing);
27 | expect(find.text('1'), findsOneWidget);
28 | });
29 | }
30 |
--------------------------------------------------------------------------------