Update README and code to set default sparkle shape to 'none' and add opacity control

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-04-15 11:24:32 +02:00
parent 0fec97163d
commit c7382c11a5
7 changed files with 257 additions and 80 deletions
+91 -35
View File
@@ -3,10 +3,12 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:holo_shiny/holo_shiny.dart';
/// Entry point for the package demo application.
void main() {
runApp(const ExampleApp());
}
/// Root app widget for the interactive shader demo.
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@@ -15,6 +17,7 @@ class ExampleApp extends StatelessWidget {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF24A6A8)),
),
home: const ExampleHome(),
@@ -22,6 +25,7 @@ class ExampleApp extends StatelessWidget {
}
}
/// Interactive screen used to test style and uniform controls.
class ExampleHome extends StatefulWidget {
const ExampleHome({super.key});
@@ -30,19 +34,26 @@ class ExampleHome extends StatefulWidget {
}
class _ExampleHomeState extends State<ExampleHome> {
/// Sensor-driven controller used by the first two demo surfaces.
late final ShinyController _sensorController;
/// Manual stream used by tilt preset buttons.
late final StreamController<Offset> _externalTiltController;
/// Controller backed by [_externalTiltController].
late final ShinyController _overrideController;
double _prismatic = 0.8;
double _sparkle = 0.8;
double _specular = 0.8;
double _diffraction = 0.8;
double _opacity = 1.0;
HolographStyle _style = HolographStyle.crackedIce;
SparkleShapeSpec _sparkleShape = SparkleShapeSpec.eightPointStar;
SparkleShapeSpec _sparkleShape = SparkleShapeSpec.none;
static const Map<String, SparkleShapeSpec> _sparkleChoices =
<String, SparkleShapeSpec>{
'None': SparkleShapeSpec.none,
'8-Point Star': SparkleShapeSpec.eightPointStar,
'5-Point Star': SparkleShapeSpec.fivePointStar,
'Rectangle': SparkleShapeSpec.rectangle,
@@ -90,6 +101,11 @@ class _ExampleHomeState extends State<ExampleHome> {
child: Shiny(
controller: _sensorController,
style: _style,
prismatic: _prismatic,
sparkle: _sparkle,
specular: _specular,
diffraction: _diffraction,
opacity: _opacity,
sparkleShape: _sparkleShape,
child: Container(
padding: const EdgeInsets.all(16),
@@ -127,6 +143,7 @@ class _ExampleHomeState extends State<ExampleHome> {
sparkle: _sparkle,
specular: _specular,
diffraction: _diffraction,
opacity: _opacity,
),
),
const SizedBox(height: 16),
@@ -169,27 +186,18 @@ class _ExampleHomeState extends State<ExampleHome> {
_diffraction = value;
});
}),
_LabeledSlider('Opacity', _opacity, (double value) {
setState(() {
_opacity = value;
});
}),
const SizedBox(height: 24),
const Text('External Tilt Stream + Custom Shape (card)'),
const SizedBox(height: 8),
Row(
children: <Widget>[
Expanded(
child: ElevatedButton(
onPressed: () =>
_externalTiltController.add(const Offset(0.7, 0.0)),
child: const Text('Tilt Right'),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: () =>
_externalTiltController.add(const Offset(-0.7, 0.0)),
child: const Text('Tilt Left'),
),
),
],
_TiltPresetPicker(
onChanged: (Offset tilt) {
_externalTiltController.add(tilt);
},
),
const SizedBox(height: 8),
Center(
@@ -197,6 +205,11 @@ class _ExampleHomeState extends State<ExampleHome> {
controller: _overrideController,
style: _style,
sparkleShape: _sparkleShape,
prismatic: _prismatic,
sparkle: _sparkle,
specular: _specular,
diffraction: _diffraction,
opacity: _opacity,
shape: const StadiumBorder(),
background: Container(
decoration: const BoxDecoration(
@@ -225,6 +238,7 @@ class _ExampleHomeState extends State<ExampleHome> {
}
class _LabeledSlider extends StatelessWidget {
/// Creates a slider row with a live numeric label.
const _LabeledSlider(this.label, this.value, this.onChanged);
final String label;
@@ -236,7 +250,7 @@ class _LabeledSlider extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(label),
Text('$label: ${value.toStringAsFixed(2)}'),
Slider(
value: value,
onChanged: onChanged,
@@ -247,6 +261,7 @@ class _LabeledSlider extends StatelessWidget {
}
class _StylePicker extends StatelessWidget {
/// Creates the style selection control.
const _StylePicker({
required this.selectedStyle,
required this.onChanged,
@@ -262,21 +277,21 @@ class _StylePicker extends StatelessWidget {
children: <Widget>[
const Text('Style'),
const SizedBox(height: 6),
Wrap(
spacing: 8,
runSpacing: 8,
children: HolographStyle.values.map((HolographStyle style) {
return ChoiceChip(
label: Text(_styleLabel(style)),
selected: selectedStyle == style,
onSelected: (bool selected) {
if (!selected) {
return;
}
onChanged(style);
},
);
}).toList(),
SegmentedButton<HolographStyle>(
segments: HolographStyle.values
.map((HolographStyle style) => ButtonSegment<HolographStyle>(
value: style,
label: Text(_styleLabel(style)),
))
.toList(),
selected: <HolographStyle>{selectedStyle},
onSelectionChanged: (Set<HolographStyle> value) {
if (value.isNotEmpty) {
onChanged(value.first);
}
},
showSelectedIcon: false,
multiSelectionEnabled: false,
),
],
);
@@ -297,6 +312,7 @@ class _StylePicker extends StatelessWidget {
}
class _SparkleShapePicker extends StatelessWidget {
/// Creates sparkle shape chips from the provided shape map.
const _SparkleShapePicker({
required this.selectedShape,
required this.choices,
@@ -336,6 +352,45 @@ class _SparkleShapePicker extends StatelessWidget {
}
}
/// Preset controls for feeding an external tilt stream.
class _TiltPresetPicker extends StatelessWidget {
const _TiltPresetPicker({required this.onChanged});
final ValueChanged<Offset> onChanged;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: FilledButton.tonalIcon(
onPressed: () => onChanged(const Offset(-0.7, 0.0)),
icon: const Icon(Icons.keyboard_double_arrow_left),
label: const Text('Tilt Left'),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed: () => onChanged(Offset.zero),
icon: const Icon(Icons.restart_alt),
label: const Text('Center'),
),
),
const SizedBox(width: 8),
Expanded(
child: FilledButton.tonalIcon(
onPressed: () => onChanged(const Offset(0.7, 0.0)),
icon: const Icon(Icons.keyboard_double_arrow_right),
label: const Text('Tilt Right'),
),
),
],
);
}
}
/// Demonstration card background for the primary example.
class _DemoCardBackground extends StatelessWidget {
const _DemoCardBackground();
@@ -390,6 +445,7 @@ class _DemoCardBackground extends StatelessWidget {
}
}
/// Small badge overlay placed on top of the shiny card.
class _RareBadge extends StatelessWidget {
const _RareBadge();