banner_slider 0.0.1
banner_slider: ^0.0.1 copied to clipboard
Reusable Flutter video, image, text, banner and auto-playing slider widget.
banner_slider #
Reusable Flutter widget for mixed-media hero banners: network video, raster images (including GIF), and SVG, with cross-fade transitions, optional page dots, visibility-aware autoplay, and a customizable overlay.
Demo #
Table of contents #
- Features
- Installation
- Quick start
- API overview
BannerSliderMedia- Timing and autoplay
- Mute and overlay (
BannerSliderMuteState) - Layout, dots, and tap
- Single-item constructor
- Breaking changes (from
videoUrlBuilder) - Example app
Features #
| Area | What you get |
|---|---|
| Media | Video (network URL), images/GIF (Image.network / Image.asset), SVG (SvgPicture.network / SvgPicture.asset). |
| Transitions | Cross-fade between slides; video preloading; raster image preloading for the next slide. |
| Generic items | BannerSlider<T> — your model type, mediaBuilder maps item → BannerSliderMedia. |
| Overlay | itemBuilder(context, index, muteState) for titles, CTAs, mute button, etc. |
| Mute | BannerSliderMuteState: muted, canMute, toggle — no dead buttons on non-video slides. |
| Dots | Built-in indicators; position (DotsPosition), optional active/inactive colors (multi-item slider). |
| Layout | padding, contentPadding, borderRadius, width, height, aspectRatio, mediaFit. |
| Behavior | loop, visibility-based play/pause, lifecycle handling, onItemTap(T item). |
Installation #
Add the package to your app pubspec.yaml (path or pub.dev once published):
dependencies:
banner_slider:
path: ../banner_slider
Then:
flutter pub get
Transitive dependencies used by this package include video_player, visibility_detector, and flutter_svg for SVG rendering.
Quick start #
import 'package:banner_slider/banner_slider.dart';
BannerSlider<MyItem>(
items: myItems,
mediaBuilder: (item) => item.media,
itemBuilder: (context, index, muteState) {
return YourOverlay(/* use muteState when muteState.canMute */);
},
);
API overview #
BannerSlider<T> #
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<T> |
required | Slide data. |
mediaBuilder |
BannerSliderMedia Function(T) |
required | Maps each item to its media + timing hints. |
itemBuilder |
ItemBuilder |
required | Overlay: (context, index, muteState). |
loop |
bool |
true |
Whether the slider wraps after the last item. |
nonTimedItemDuration |
Duration |
3s |
Fallback display time for image/svg (and for video when not playing to end — see timing). |
defaultPlayVideoToEnd |
bool |
true |
If true, video uses natural end unless overridden per item. |
loadingBuilder |
Widget Function(BuildContext, int)? |
default spinner | Builds the loading UI for the current slide while media is being prepared. |
loaderColor |
Color? |
theme default | Colors the built-in loader when loadingBuilder is not provided. |
onItemTap |
ValueChanged<T>? |
null |
Called when the banner is tapped (current item). |
padding |
EdgeInsetsGeometry |
horizontal 16 |
Outer padding around the slider. |
contentPadding |
EdgeInsetsGeometry |
12 all sides |
Padding for overlay content (and dot alignment). |
borderRadius |
double |
16 |
Corner radius of the clipped banner. |
width / height |
double? |
null |
Optional SizedBox around the aspect-ratio child. |
aspectRatio |
double |
21/9 |
Banner aspect ratio. |
mediaFit |
BoxFit |
fitWidth |
How video/image/svg fill the frame. |
dotActiveColor / dotInactiveColor |
Color? |
primary / white 50% | Dot colors when items.length > 1 (inactive defaults to Colors.white at 50% opacity). |
dotsPosition |
DotsPosition |
bottomCenter |
Corner/edge alignment for dots. |
BannerSlider<T>.single(...) #
Convenience factory: same behavior as BannerSlider(items: [item], ...) but without dotActiveColor, dotInactiveColor, or dotsPosition parameters. Dots only render when there is more than one slide, so these are omitted from .single to keep the API small.
BannerSliderMedia #
Use the constructors below from mediaBuilder:
| Constructor | Use case |
|---|---|
BannerSliderMedia.video({ url, displayDuration?, playToEnd? }) |
Network MP4 (etc.). |
BannerSliderMedia.imageNetwork({ url, displayDuration? }) |
Remote PNG/JPEG/WebP/GIF. |
BannerSliderMedia.imageAsset({ path, displayDuration? }) |
Asset raster image. |
BannerSliderMedia.svgNetwork({ url, displayDuration? }) |
Remote SVG. |
BannerSliderMedia.svgAsset({ path, displayDuration? }) |
Asset SVG. |
Optional fields:
displayDuration— Forces a timed slide; when set, this duration is used for that slide (including video — transitions early instead of waiting for end-of-file).playToEnd(video only) — Overrides sliderdefaultPlayVideoToEndfor that item (true= wait for video end if nodisplayDuration).
Timing and autoplay #
-
Image / GIF / SVG
- If
displayDurationis set on the media → use it. - Else → use
nonTimedItemDuration(default 3 seconds).
- If
-
Video
- If
displayDurationis set → advance after that duration (video does not need to finish). - Else, if
playToEnd ?? defaultPlayVideoToEndis true → advance near end of video (existing end-detection behavior). - Else → use
nonTimedItemDurationas the video’s fixed time on screen.
- If
-
Visibility
Playback pauses when the banner is not sufficiently visible; resumes when it is (viavisibility_detector). -
Single video +
loop: true
Native looping may apply when there is only one video item and timing is “play to end” (see implementation in package).
Mute and overlay (BannerSliderMuteState) #
The overlay builder receives:
BannerSliderMuteState {
bool muted; // Last known muted flag for video volume
bool canMute; // true only when current slide is video with initialized controller
VoidCallback? toggle; // null when !canMute; otherwise toggles mute
}
Recommended UI: show a volume control only when muteState.canMute; use muteState.toggle as onPressed. For image/svg slides, canMute is false so you do not show a misleading mute button.
Layout, dots, and tap #
- Tap: the full banner area triggers
onItemTapwith the currentTitem (your model), not the fading layer. - Dots: shown only when
items.length > 1. Positions:topLeft,topCenter,topRight,bottomLeft,bottomCenter,bottomRight. - Gradient: the package draws a subtle bottom-to-top gradient behind your overlay for readability; your
itemBuildercontent sits on top. - Loading UI: customize the loader with
loadingBuilder; or keep the built-in spinner and change its color withloaderColor.
Single-item constructor #
BannerSlider<Item>.single(
item: onlyItem,
mediaBuilder: (item) => item.media,
itemBuilder: (context, index, muteState) => const SizedBox.shrink(),
onItemTap: (item) { },
);
Useful for one full-width or one-card banner without duplicating [item] yourself.
Breaking changes (from videoUrlBuilder) #
Older versions used videoUrlBuilder: String Function(T). That API is removed in favor of:
mediaBuilder: BannerSliderMedia Function(T item)
Map each item to BannerSliderMedia.video(url: ...) or another media constructor. Overlay API also moved from (muted, onMute) to (muteState).
Full example #
import 'package:banner_slider/banner_slider.dart';
import 'package:flutter/material.dart';
class PromoBanner {
final String title;
final BannerSliderMedia media;
const PromoBanner({required this.title, required this.media});
}
const items = <PromoBanner>[
PromoBanner(
title: 'Video',
media: BannerSliderMedia.video(
url: 'https://example.com/video.mp4',
playToEnd: true,
),
),
PromoBanner(
title: 'GIF',
media: BannerSliderMedia.imageNetwork(
url: 'https://example.com/animated.gif',
displayDuration: Duration(seconds: 2),
),
),
PromoBanner(
title: 'SVG',
media: BannerSliderMedia.svgNetwork(
url: 'https://example.com/logo.svg',
),
),
];
// In your widget tree:
BannerSlider<PromoBanner>(
items: items,
mediaBuilder: (item) => item.media,
nonTimedItemDuration: const Duration(seconds: 3),
defaultPlayVideoToEnd: true,
mediaFit: BoxFit.cover,
dotsPosition: DotsPosition.topRight,
dotActiveColor: Colors.white,
dotInactiveColor: Colors.white54,
itemBuilder: (context, index, muteState) {
final item = items[index];
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(item.title, style: const TextStyle(color: Colors.white)),
if (muteState.canMute)
IconButton(
onPressed: muteState.toggle,
icon: Icon(
muteState.muted ? Icons.volume_off : Icons.volume_up,
),
),
],
);
},
onItemTap: (item) {},
);
Example app #
A runnable demo lives under example/ (mixed media, dots, horizontal list, etc.):
cd example
flutter run
Use it as a reference for mediaBuilder, timing, and muteState wiring.