Compare commits

..

No commits in common. "master" and "0.0.1" have entirely different histories.

24 changed files with 153 additions and 430 deletions

View file

@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "pub"
directory: "/"
schedule:
interval: "daily"

View file

@ -1,14 +0,0 @@
name: Iconica Standard Component CI Workflow
# Workflow Caller version: 2.0.0
on:
pull_request:
workflow_dispatch:
jobs:
call-global-iconica-workflow:
uses: Iconica-Development/.github/.github/workflows/component-ci.yml@master
secrets: inherit
permissions: write-all
with:
subfolder: "." # add optional subfolder to run workflow in

View file

@ -1,14 +0,0 @@
name: Iconica Standard Component Documentation Workflow
# Workflow Caller version: 1.0.0
on:
release:
types: [published]
workflow_dispatch:
jobs:
call-iconica-component-documentation-workflow:
uses: Iconica-Development/.github/.github/workflows/component-documentation.yml@master
secrets: inherit
permissions: write-all

6
.gitignore vendored
View file

@ -20,8 +20,4 @@ doc/api/
*.js.deps
*.js.map
.idea
# FVM Version Cache
.fvm/
.fvmrc
.idea

View file

@ -1,14 +1,3 @@
## 0.3.1
* Added Iconica CI and Iconica Linter
## 0.3.0
* Added option for backwards infinite scrolling and intial page
## 0.1.0
* add opacity as an option to the card transform
## 0.0.1
* TODO: Describe initial release.

View file

@ -1,7 +0,0 @@
* Every sort of widget can be animated as a card.
* For the animation the following properties can be set between animation frames:
this.x = 0,
this.y = 0,
this.angle = 0,
this.scale = 1,
this.opacity = 1.0,

10
LICENSE
View file

@ -1,9 +1 @@
Copyright (c) 2022 Iconica, All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
TODO: Add your license here.

View file

@ -1,21 +1,18 @@
[![pub package](https://img.shields.io/pub/v/flutter_carousel.svg)](https://github.com/Iconica-Development) [![Build status](https://img.shields.io/github/workflow/status/Iconica-Development/flutter_carousel/CI)](https://github.com/Iconica-Development/flutter_carousel/actions/new) [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart)
# Carousel
Carousel widget. Makes it easier to create card carousels using a list of transforms.
Each card can change its rotation, position and scale when swiping the cards.
![Carousel GIF](carousel.gif)
Figma Design that defines this component (only accessible for Iconica developers): https://www.figma.com/file/4WkjwynOz5wFeFBRqTHPeP/Iconica-Design-System?type=design&node-id=357%3A3335&mode=design&t=XulkAJNPQ32ARxWh-1
Supports all platforms.
![Demo video](demo.gif)
## Usage
To use this package, add `carousel` as a dependency in your pubspec.yaml file.
To use this package, add `carousel` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels).
## How to use
### Example
See the [Example Code](example/lib/main.dart) for an example on how to use this package.
See [Example Code](example/lib/main.dart) for more info.
## Issues
@ -27,4 +24,4 @@ If you would like to contribute to the plugin (e.g. by improving the documentati
## Author
This `carousel` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
This carousel for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>

View file

@ -1,9 +1 @@
include: package:flutter_iconica_analysis/analysis_options.yaml
# Possible to overwrite the rules from the package
analyzer:
exclude:
linter:
rules:
include: package:flutter_lints/flutter.yaml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

BIN
demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 MiB

View file

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>9.0</string>
</dict>
</plist>

View file

@ -272,7 +272,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -350,7 +350,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -399,7 +399,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;

View file

@ -1,4 +0,0 @@
extension CapitalizeExtension on String {
String capitalize() =>
isNotEmpty ? "${this[0].toUpperCase()}${substring(1)}" : this;
}

View file

@ -1,12 +1,7 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:carousel/carousel.dart';
import 'package:carousel_example/pokemon.dart';
import 'package:carousel_example/pokemon_card.dart';
import 'package:carousel_example/pokemon_types.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
@ -26,65 +21,14 @@ class _CarouselExampleAppState extends State<CarouselExampleApp> {
Pokemon? selected;
final List<Pokemon> pokemons = [
Pokemon(
name: 'Bulbasaur',
nr: 1,
types: [
PokemonType.grass,
PokemonType.poison,
],
),
Pokemon(
name: 'Charmander',
nr: 4,
types: [
PokemonType.fire,
],
),
Pokemon(
name: 'Squirtle',
nr: 7,
types: [
PokemonType.water,
],
),
Pokemon(
name: 'Caterpie',
nr: 10,
types: [
PokemonType.bug,
],
),
Pokemon(
name: 'Pidgey',
nr: 16,
types: [
PokemonType.normal,
PokemonType.flying,
],
),
Pokemon(
name: 'Pikachu',
nr: 25,
types: [
PokemonType.electric,
],
),
Pokemon(
name: 'Machop',
nr: 66,
types: [
PokemonType.fighting,
],
),
Pokemon(
name: 'Geodude',
nr: 74,
types: [
PokemonType.rock,
PokemonType.ground,
],
),
Pokemon(name: 'Bulbasaur', nr: 1, types: ['grass', 'poison']),
Pokemon(name: 'Charmander', nr: 4, types: ['fire']),
Pokemon(name: 'Squirtle', nr: 7, types: ['water']),
Pokemon(name: 'Caterpie', nr: 10, types: ['bug']),
Pokemon(name: 'Pidgey', nr: 16, types: ['normal', 'flying']),
Pokemon(name: 'Pikachu', nr: 25, types: ['electric']),
Pokemon(name: 'Machop', nr: 66, types: ['fighting']),
Pokemon(name: 'Geodude', nr: 74, types: ['rock', 'ground']),
];
Widget _buildCard(BuildContext context, int index) {
@ -106,8 +50,7 @@ class _CarouselExampleAppState extends State<CarouselExampleApp> {
body: Stack(
alignment: Alignment.bottomCenter,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
SafeArea(
child: ListView(
children: [
const SizedBox(height: 50),
@ -117,47 +60,16 @@ class _CarouselExampleAppState extends State<CarouselExampleApp> {
onCardClick: _onClick,
transforms: [
CardTransform(
x: 220,
y: -85,
angle: -math.pi / 12,
scale: 0.3,
opacity: 0.2,
),
x: 220, y: -85, angle: -math.pi / 12, scale: 0.3),
CardTransform(
x: 230,
y: -80,
angle: math.pi / 12,
scale: 0.5,
opacity: 0.4,
),
x: 230, y: -80, angle: math.pi / 12, scale: 0.5),
CardTransform(
x: 200,
y: -75,
angle: -math.pi / 12,
scale: 0.6,
opacity: 0.6,
),
x: 200, y: -75, angle: -math.pi / 12, scale: 0.6),
CardTransform(
x: 110,
y: -60,
angle: math.pi / 12,
scale: 0.7,
opacity: 0.8,
),
x: 110, y: -60, angle: math.pi / 12, scale: 0.7),
CardTransform(x: 0, y: 0, angle: 0, scale: 1),
CardTransform(
x: 0,
y: 0,
angle: 0,
scale: 1,
opacity: 1.0,
),
CardTransform(
x: -270,
y: 70,
angle: -math.pi / 50,
scale: 1,
opacity: 0.5,
),
x: -270, y: 70, angle: -math.pi / 50, scale: 1),
],
builder: _buildCard,
),
@ -178,7 +90,7 @@ class _CarouselExampleAppState extends State<CarouselExampleApp> {
),
if (selected != null)
Padding(
padding: const EdgeInsets.only(bottom: 20),
padding: const EdgeInsets.only(bottom: 10),
child: Text('Clicked: ${selected!.name}'),
),
],

View file

@ -1,9 +1,3 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:carousel_example/pokemon_types.dart';
class Pokemon {
Pokemon({
required this.name,
@ -12,5 +6,5 @@ class Pokemon {
});
String name;
int nr;
List<PokemonType> types = const [];
List<String> types = const [];
}

View file

@ -1,10 +1,4 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:carousel_example/extensions/string.dart';
import 'package:carousel_example/pokemon.dart';
import 'package:carousel_example/pokemon_types.dart';
import 'package:flutter/material.dart';
class PokemonCard extends StatelessWidget {
@ -22,11 +16,9 @@ class PokemonCard extends StatelessWidget {
width: MediaQuery.of(context).size.width / 2.2,
height: 220,
decoration: BoxDecoration(
color: Colors.white,
color: Colors.blue.shade700,
boxShadow: const [
BoxShadow(
blurRadius: 8,
),
BoxShadow(blurRadius: 4),
],
borderRadius: BorderRadius.circular(15),
),
@ -44,12 +36,29 @@ class PokemonCard extends StatelessWidget {
),
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 8),
child: Text('Type', style: textStyle),
child: Text('Types', style: textStyle),
),
Row(
children: [
for (var type in pokemon.types) ...[
TypeBadge(type: type),
Container(
height: 25,
width: 60,
margin: const EdgeInsets.only(right: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Color(type.hashCode).withAlpha(255),
),
child: Center(
child: Text(
type,
style: const TextStyle(
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
)
],
],
),
@ -59,77 +68,3 @@ class PokemonCard extends StatelessWidget {
);
}
}
class TypeBadge extends StatelessWidget {
const TypeBadge({
super.key,
required this.type,
});
final PokemonType type;
@override
Widget build(BuildContext context) {
return Container(
height: 25,
width: 60,
margin: const EdgeInsets.only(right: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: getTypeColor(type),
),
child: Center(
child: Text(
type.name.capitalize(),
style: const TextStyle(
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
);
}
Color getTypeColor(PokemonType type) {
switch (type) {
case PokemonType.fire:
return const Color(0xFFF08030);
case PokemonType.water:
return const Color(0xFF6890F0);
case PokemonType.grass:
return const Color(0xFF78C850);
case PokemonType.normal:
return const Color(0xFFA8A878);
case PokemonType.electric:
return const Color(0xFFF8D030);
case PokemonType.ice:
return const Color(0xFF98D8D8);
case PokemonType.fighting:
return const Color(0xFFC03028);
case PokemonType.poison:
return const Color(0xFFA040A0);
case PokemonType.ground:
return const Color(0xFFE0C068);
case PokemonType.flying:
return const Color(0xFFA890F0);
case PokemonType.psychic:
return const Color(0xFFF85888);
case PokemonType.bug:
return const Color(0xFFA8B820);
case PokemonType.rock:
return const Color(0xFFB8A038);
case PokemonType.ghost:
return const Color(0xFF705898);
case PokemonType.dark:
return const Color(0xFF705848);
case PokemonType.dragon:
return const Color(0xFF7038F8);
case PokemonType.steel:
return const Color(0xFFB8B8D0);
case PokemonType.fairy:
return const Color(0xFFEE99AC);
default:
return const Color(0xFF68A090);
}
}
}

View file

@ -1,20 +0,0 @@
enum PokemonType {
fire,
water,
grass,
normal,
electric,
ice,
fighting,
poison,
ground,
flying,
psychic,
bug,
rock,
ghost,
dark,
dragon,
steel,
fairy,
}

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
/// A Flutter package for creating a carousel widget.
library carousel;
export 'package:carousel/src/carousel.dart';

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:carousel/src/models/card_transform.dart';
import 'package:carousel/src/widgets/carousel_card.dart';
import 'package:flutter/gestures.dart';
@ -11,8 +7,7 @@ typedef CarouselCardBuilder = Widget Function(BuildContext context, int index);
class Carousel extends StatefulWidget {
/// Animated cards by swiping.
/// Each card can change its rotation, position
/// and scale when swiping the cards.
/// Each card can change its rotation, position and scale when swiping the cards.
/// Transform path can be privided using [transforms]
const Carousel({
required this.transforms,
@ -22,10 +17,8 @@ class Carousel extends StatefulWidget {
this.onPageChanged,
this.alignment = AlignmentDirectional.topStart,
this.onCardClick,
this.initialPage = 0,
this.allowInfiniteScrollingBackwards = false,
super.key,
});
Key? key,
}) : super(key: key);
/// A list of transforms to calculate the position of the card when swiping.
/// Every item in the list is one of the possible card positions.
@ -34,8 +27,7 @@ class Carousel extends StatefulWidget {
/// The index of the transform card which acts as the selected card.
final int selectableCardId;
/// Builder for the card given a [context] and a [index] to
/// identify the right card.
/// Builder for the card given a [context] and a [index] to identify the right card.
final CarouselCardBuilder builder;
/// Called when selected card is changed to the next one.
@ -50,31 +42,16 @@ class Carousel extends StatefulWidget {
/// Size of the pageview used to capture swipe gestures.
final double pageViewHeight;
/// The page to show when first creating the [Carousel].
final int initialPage;
/// Whether to allow infinite scrolling backwards. Defaults to false. If true,
/// this works by using a very large number of pages (10000).
/// Works in conjunction with [initialPage].
final bool allowInfiniteScrollingBackwards;
@override
State<Carousel> createState() => _CarouselState();
}
class _CarouselState extends State<Carousel> {
late PageController _pageController;
final PageController _pageController = PageController(initialPage: 0);
double _currentPage = 0;
@override
void initState() {
_pageController = PageController(
initialPage: widget.allowInfiniteScrollingBackwards
? 10000 + widget.initialPage
: widget.initialPage,
);
_currentPage = _pageController.initialPage.toDouble();
_pageController.addListener(() {
_currentPage = _pageController.page!;
});
@ -88,36 +65,38 @@ class _CarouselState extends State<Carousel> {
}
@override
Widget build(BuildContext context) => Stack(
alignment: widget.alignment,
children: [
AnimatedBuilder(
animation: _pageController,
builder: (context, _) {
var transitionPos = _currentPage % 1;
var index = _currentPage.floor();
var length = widget.transforms.length - 1;
Widget build(BuildContext context) {
return Stack(
alignment: widget.alignment,
children: [
AnimatedBuilder(
animation: _pageController,
builder: (context, _) {
final transitionPos = _currentPage % 1;
final index = _currentPage.floor();
final length = widget.transforms.length - 1;
return Stack(
children: [
for (var i = 0; i < length; i++) ...[
CarouselCard(
cardTransform: widget.transforms[i]
.transform(widget.transforms[i + 1], transitionPos),
child: widget.builder.call(context, index - i),
),
],
return Stack(
children: [
for (var i = 0; i < length; i++) ...[
CarouselCard(
cardTransform: widget.transforms[i]
.transform(widget.transforms[i + 1], transitionPos),
child: widget.builder.call(context, index - i),
),
],
);
},
),
SizedBox(
height: widget.pageViewHeight,
child: PageView.builder(
scrollBehavior: _MouseSwipeOnWeb(),
onPageChanged: widget.onPageChanged,
controller: _pageController,
itemBuilder: (context, index) => Visibility(
],
);
},
),
SizedBox(
height: widget.pageViewHeight,
child: PageView.builder(
scrollBehavior: _MouseSwipeOnWeb(),
onPageChanged: widget.onPageChanged,
controller: _pageController,
itemBuilder: (context, index) {
return Visibility(
visible: false,
maintainState: true,
maintainAnimation: true,
@ -137,11 +116,13 @@ class _CarouselState extends State<Carousel> {
),
],
),
),
),
);
},
),
],
);
),
],
);
}
}
class _MouseSwipeOnWeb extends MaterialScrollBehavior {

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
class CardTransform {
/// Used by [Carousel] to build cards on the correct position.
CardTransform({
@ -9,33 +5,23 @@ class CardTransform {
this.y = 0,
this.angle = 0,
this.scale = 1,
this.opacity = 1.0,
}) : assert(
opacity >= 0,
'Opacity cannot be negative',
),
assert(
opacity <= 1.0,
'Opacity needs to be between 0.0 and 1.0',
);
});
double x;
double y;
double angle;
double scale;
double opacity;
/// [transitionPos] is a position value of a swipe for example.
/// [other] is the position, scale, rotation
/// which the current [CardTransform] need to be transformed to.
CardTransform transform(CardTransform other, double transitionPos) =>
CardTransform(
x: _transformValue(x, other.x, transitionPos),
y: _transformValue(y, other.y, transitionPos),
angle: _transformValue(angle, other.angle, transitionPos),
scale: _transformValue(scale, other.scale, transitionPos),
opacity: _transformValue(opacity, other.opacity, transitionPos),
);
CardTransform transform(CardTransform other, double transitionPos) {
return CardTransform(
x: _transformValue(x, other.x, transitionPos),
y: _transformValue(y, other.y, transitionPos),
angle: _transformValue(angle, other.angle, transitionPos),
scale: _transformValue(scale, other.scale, transitionPos),
);
}
double _transformValue(double valueA, double valueB, double transformPos) =>
valueA - ((valueA - valueB) * transformPos);

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:carousel/src/models/card_transform.dart';
import 'package:flutter/material.dart';
@ -10,24 +6,23 @@ class CarouselCard extends StatelessWidget {
const CarouselCard({
required this.cardTransform,
required this.child,
super.key,
});
Key? key,
}) : super(key: key);
final CardTransform cardTransform;
final Widget child;
@override
Widget build(BuildContext context) => Transform.translate(
offset: Offset(cardTransform.x, cardTransform.y),
child: Transform.rotate(
angle: cardTransform.angle,
child: Transform.scale(
scale: cardTransform.scale,
child: Opacity(
opacity: cardTransform.opacity,
child: child,
),
),
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(cardTransform.x, cardTransform.y),
child: Transform.rotate(
angle: cardTransform.angle,
child: Transform.scale(
scale: cardTransform.scale,
child: child,
),
);
),
);
}
}

View file

@ -1,11 +1,10 @@
name: carousel
description: card carousel
version: 0.3.1
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
version: 0.0.1
homepage:
environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=2.17.6 <3.0.0"
flutter: ">=1.17.0"
dependencies:
@ -15,9 +14,41 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
flutter_iconica_analysis:
git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages

View file

@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:math';
import 'package:carousel/src/models/card_transform.dart';