A Flet custom control that wraps the powerful carousel_slider_plus Flutter package, bringing a fully featured image/content carousel to your Flet apps.
- Horizontal and vertical carousel sliding
- Auto-play with configurable interval, duration, and curve
- Infinite scroll
- Center page enlargement with multiple strategies
- Built-in dot indicator with active/inactive color control, scrollable with fade edges
- Lazy (on-demand) or eager item building
- Full programmatic control:
animate_to,next,previous,jump_to - Python-side event callbacks:
on_change,on_scrolled
Since this package is not yet published on PyPI, install it directly from the source:
# Clone the repository
git clone https://github.com/yourusername/flet_carousel_slider.git
# Install locally
pip install -e ./flet_carousel_sliderOnce published to PyPI, installation will be:
pip install flet_carousel_slider
import flet as ft
from flet_carousel_slider import *
def main(page: ft.Page):
page.add(
Carousel(
controls=[
ft.Image(src="https://picsum.photos/id/1/400/300", fit=ft.ImageFit.COVER),
ft.Image(src="https://picsum.photos/id/2/400/300", fit=ft.ImageFit.COVER),
ft.Image(src="https://picsum.photos/id/3/400/300", fit=ft.ImageFit.COVER),
],
aspect_ratio=16 / 9,
auto_play=True,
enable_infinite_scroll=True,
enable_indicator=True,
)
)
ft.app(main)import flet as ft
from flet_carousel_slider import (
Carousel,
AnimateToCurve,
EnlargeStrategy,
ClipBehavior,
ScrollDirection,
CarouselPageChangedReason,
)
SLIDES = [
("🏔️ Mountains", ft.Colors.BLUE_700),
("🌊 Ocean", ft.Colors.CYAN_600),
("🌲 Forest", ft.Colors.GREEN_700),
("🏜️ Desert", ft.Colors.ORANGE_700),
("❄️ Arctic", ft.Colors.INDIGO_300),
("🌋 Volcano", ft.Colors.RED_700),
("🌲 Forest", ft.Colors.GREEN_700),
("🏜️ Desert", ft.Colors.ORANGE_700),
("❄️ Arctic", ft.Colors.INDIGO_300),
("🌋 Volcano", ft.Colors.RED_700),
("🌲 Forest", ft.Colors.GREEN_700),
("🏜️ Desert", ft.Colors.ORANGE_700),
("❄️ Arctic", ft.Colors.INDIGO_300),
("🌋 Volcano", ft.Colors.RED_700),
("🌲 Forest", ft.Colors.GREEN_700),
("🏜️ Desert", ft.Colors.ORANGE_700),
("❄️ Arctic", ft.Colors.INDIGO_300),
("🌋 Volcano", ft.Colors.RED_700),
]
def slide_card(label: str, color: str) -> ft.Container:
return ft.Container(
content=ft.Text(label, size=22, weight=ft.FontWeight.BOLD, color=ft.Colors.WHITE),
bgcolor=color,
border_radius=16,
alignment=ft.alignment.center,
margin=ft.margin.symmetric(horizontal=8),
)
def main(page: ft.Page):
page.title = "carousel_slider 5.1.2 – Flet demo"
page.theme = ft.Theme(use_material3=True)
page.padding = 20
page.scroll = ft.ScrollMode.AUTO
page.window.always_on_top = True
# ── status log ────────────────────────────────────────────────────
status = ft.Text("", size=13, italic=True, color=ft.Colors.GREY_700)
def log(msg: str):
status.value = msg
page.update()
# ── event handlers ────────────────────────────────────────────────
def on_changed(e):
reason_label = {
CarouselPageChangedReason.TIMED: "autoplay",
CarouselPageChangedReason.MANUAL: "swipe",
CarouselPageChangedReason.CONTROLLER: "controller",
}.get(e.reason, e.reason.value)
log(f"on_change → index={e.index} reason={reason_label}")
def on_scrolled(e):
# fires very frequently — only log every ~0.5 units to avoid spam
if abs(e.value % 0.5) < 0.05:
log(f"on_scrolled → {e.value:.2f}")
# ── carousel ──────────────────────────────────────────────────────
carousel = Carousel(
controls=[slide_card(label, color) for label, color in SLIDES],
# layout
height=200,
viewport_fraction=0.85,
initial_page=0,
# infinite / reverse
enable_infinite_scroll=False,
reverse=False,
# autoplay
auto_play=True,
auto_play_interval=3000,
auto_play_animation_duration=800,
auto_play_curve=AnimateToCurve.FAST_OUT_SLOW_IN,
pause_auto_play_on_touch=True,
pause_auto_play_on_manual_navigate=True,
pause_auto_play_in_finite_scroll=False,
# enlarge
enlarge_center_page=True,
enlarge_strategy=EnlargeStrategy.SCALE,
enlarge_factor=0.3,
# misc
page_snapping=True,
animate_to_closest=True,
disable_center=False,
pad_ends=True,
scroll_direction=ScrollDirection.HORIZONTAL,
clip_behavior=ClipBehavior.HARD_EDGE,
# events
on_change=on_changed,
on_scrolled=on_scrolled,
disablegesture=False,
build_on_demand=True,
indicatorwidth=140,
indicator_active_color="yellow",
indicator_inactive_color="green"
)
# ── controller buttons ────────────────────────────────────────────
def go_prev(e):
carousel.previous_page(duration=400, curve=AnimateToCurve.EASE_IN_OUT)
def go_next(e):
carousel.next_page(duration=400, curve=AnimateToCurve.EASE_IN_OUT)
def go_page_0(e):
carousel.animate_to_page(0, duration=600, curve=AnimateToCurve.EASE_OUT)
def go_page_3(e):
carousel.animate_to_page(3, duration=600, curve=AnimateToCurve.BOUNCE_OUT)
def jump_last(e):
carousel.jump_to_page(len(SLIDES) - 1)
def toggle_autoplay(e):
carousel.auto_play = not carousel.auto_play
btn_autoplay.text = (
"⏸ Pause autoplay" if carousel.auto_play else "▶ Resume autoplay"
)
page.update()
btn_autoplay = ft.ElevatedButton("⏸ Pause autoplay", on_click=toggle_autoplay)
page.add(
ft.Text("carousel_slider 5.1.2 – Flet", size=26, weight=ft.FontWeight.BOLD),
ft.Container(content=carousel, height=220),
ft.Divider(height=16),
ft.Text("Controller methods", size=16, weight=ft.FontWeight.W_600),
ft.Row([
ft.ElevatedButton("◀ Prev", on_click=go_prev),
ft.ElevatedButton("Next ▶", on_click=go_next),
ft.ElevatedButton("→ Page 0", on_click=go_page_0),
ft.ElevatedButton("→ Page 3", on_click=go_page_3),
ft.ElevatedButton("Jump last", on_click=jump_last),
btn_autoplay,
], wrap=True, spacing=8),
ft.Divider(height=16),
ft.Text("Events", size=16, weight=ft.FontWeight.W_600),
status,
)
ft.app(target=main)def on_slide_change(e):
print(f"Slide changed: index={e.data}")
def on_slide_scrolled(e):
print(f"Scroll position: {e.data}")
CarouselSlider(
controls=[...],
on_change=on_slide_change,
on_scrolled=on_slide_scrolled,
)| Property | Type | Default | Description |
|---|---|---|---|
controls |
List[Control] |
required | The list of widgets to display as slides |
height |
float |
None |
Fixed height of the carousel. If set, overrides aspect_ratio |
aspect_ratio |
float |
16/9 |
Aspect ratio used when height is not set |
viewport_fraction |
float |
0.8 |
Fraction of the viewport each slide occupies (e.g. 1.0 for full width) |
initial_page |
int |
0 |
The index of the slide shown on first load |
enable_infinite_scroll |
bool |
True |
Whether the carousel loops infinitely |
animate_to_closest |
bool |
True |
Whether to animate to the closest page on scroll end |
reverse |
bool |
False |
Reverse the scroll direction |
page_snapping |
bool |
True |
Snap to page boundaries on scroll end |
scroll_direction |
str |
"horizontal" |
Scroll axis — "horizontal" or "vertical" |
clip_behavior |
str |
"hardEdge" |
Flutter clip mode — "none", "hardEdge", "antiAlias", "antiAliasWithSaveLayer" |
disable_gesture |
bool |
False |
Disable swipe gestures (useful when controlling programmatically only) |
disable_center |
bool |
False |
Disable centering of slides |
pad_ends |
bool |
True |
Add padding at the ends of the carousel |
build_on_demand |
bool |
False |
Build slides lazily (on-demand) for better performance with large lists |
| Property | Type | Default | Description |
|---|---|---|---|
auto_play |
bool |
False |
Enable automatic sliding |
auto_play_interval |
int |
4000 |
Time in milliseconds between auto-play slides |
auto_play_animation_duration |
int |
800 |
Duration in milliseconds of the auto-play slide animation |
auto_play_curve |
str |
"fastOutSlowIn" |
Animation curve for auto-play. See Curves |
pause_auto_play_on_touch |
bool |
True |
Pause auto-play when the user touches the carousel |
pause_auto_play_on_manual_navigate |
bool |
True |
Pause auto-play when navigating programmatically |
pause_auto_play_in_finite_scroll |
bool |
False |
Pause auto-play at the ends when infinite scroll is disabled |
| Property | Type | Default | Description |
|---|---|---|---|
enlarge_center_page |
bool |
False |
Enlarge the center/active slide |
enlarge_strategy |
str |
"scale" |
How the center page is enlarged — "scale", "height", "zoom" |
enlarge_factor |
float |
0.3 |
How much the center page is enlarged (0.0 to 1.0) |
| Property | Type | Default | Description |
|---|---|---|---|
enable_indicator |
bool |
True |
Show dot indicators below the carousel |
indicator_active_color |
ft.Colors |
theme default | Color of the currently active dot |
indicator_inactive_color |
ft.Colors |
theme default | Color of the inactive dots |
indicator_row_width |
float |
None |
Max width of the indicator row. If None, takes full available width |
| Event | Data | Description |
|---|---|---|
on_change |
"index:reason" |
Fired when the active slide changes. reason is one of timed, manual, controller |
on_scrolled |
"0.0000" |
Fired continuously while scrolling with the current scroll offset |
| Method | Parameters | Description |
|---|---|---|
animate_to(index) |
index: int, duration: int = 300, curve: str = "fastOutSlowIn" |
Animate to a specific slide index |
next() |
duration: int = 300, curve: str = "fastOutSlowIn" |
Go to the next slide |
previous() |
duration: int = 300, curve: str = "fastOutSlowIn" |
Go to the previous slide |
jump_to(index) |
index: int |
Jump to a slide instantly with no animation |
The following curve names are accepted by auto_play_curve and animation methods:
"linear", "easeIn", "easeOut", "easeInOut", "bounceIn", "bounceOut", "elasticIn", "elasticOut", "decelerate", "fastOutSlowIn"
import flet as ft
from flet_carousel_slider import CarouselSlider
def main(page: ft.Page):
page.padding = 0
carousel = CarouselSlider(
controls=[
ft.Container(
content=ft.Text(f"Slide {i+1}", size=30, color=ft.Colors.WHITE),
bgcolor=[ft.Colors.BLUE, ft.Colors.RED, ft.Colors.GREEN, ft.Colors.ORANGE][i],
alignment=ft.alignment.center,
expand=True,
)
for i in range(4)
],
aspect_ratio=16 / 9,
auto_play=True,
auto_play_interval=3000,
auto_play_curve="easeInOut",
enable_infinite_scroll=True,
enlarge_center_page=True,
enlarge_strategy="zoom",
viewport_fraction=0.85,
enable_indicator=True,
indicator_active_color=ft.Colors.BLACK,
indicator_inactive_color=ft.Colors.GREY,
on_change=lambda e: print(f"Changed to: {e.data}"),
)
page.add(carousel)
ft.app(main)MIT