liquid_bottle 0.0.4 copy "liquid_bottle: ^0.0.4" to clipboard
liquid_bottle: ^0.0.4 copied to clipboard

Create stunning liquid bottle animations with realistic physics. Supports custom shapes, branded designs, and interactive liquid volume control.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:liquid_bottle/liquid_bottle.dart';

void main() {
  runApp(const LiquidBottleExampleApp());
}

class LiquidBottleExampleApp extends StatelessWidget {
  const LiquidBottleExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Liquid Inventory',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        brightness: Brightness.dark,
        scaffoldBackgroundColor: const Color(0xFF0F0F0F),
        primaryColor: Colors.amber,
      ),
      home: const LiquidBottleShowcase(),
    );
  }
}

class LiquidBottleShowcase extends StatefulWidget {
  const LiquidBottleShowcase({super.key});

  @override
  State<LiquidBottleShowcase> createState() => _LiquidBottleShowcaseState();
}

class _BottleConfig {
  final String id;
  final String name;
  final String volume;
  final BrandedBottleType type;
  final double defaultFill;
  final Offset labelOffset;
  final double labelScale;

  const _BottleConfig({
    required this.id,
    required this.name,
    required this.volume,
    required this.type,
    required this.defaultFill,
    this.labelOffset = Offset.zero,
    this.labelScale = 1.0,
  });
}

class _LiquidBottleShowcaseState extends State<LiquidBottleShowcase> {
  late PageController _pageController;
  int _currentIndex = 0; // Starts at Bacardi

  final Map<String, double> _fillLevels = {};

  final List<_BottleConfig> _bottles = [
    _BottleConfig(
      id: 'bacardi',
      name: "BACARDÍ",
      volume: "750ml • 25.4 oz",
      type: BrandedBottleType.bacardi,
      defaultFill: 0.75,
    ),
    _BottleConfig(
      id: 'bombay',
      name: "BOMBAY SAPPHIRE",
      volume: "750ml • 25.4 oz",
      type: BrandedBottleType.bombaySapphire,
      defaultFill: 0.65,
      labelOffset: const Offset(0, 10),
      labelScale: 0.5,
    ),
    _BottleConfig(
      id: 'jose',
      name: "JOSE CUERVO",
      volume: "750ml • 25.4 oz",
      type: BrandedBottleType.joseCuervo,
      defaultFill: 0.70,
      labelScale: 1.5,
      labelOffset: const Offset(-8, 0),
    ),
    _BottleConfig(
      id: 'patron',
      name: "PATRÓN",
      volume: "750ml • 25.4 oz",
      type: BrandedBottleType.patron,
      defaultFill: 0.60,
    ),
  ];

  @override
  void initState() {
    super.initState();
    for (var bottle in _bottles) {
      _fillLevels[bottle.id] = bottle.defaultFill;
    }

    _pageController = PageController(
      initialPage: _currentIndex,
      viewportFraction: 0.65,
    );
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  String _getCurrentId(int index) {
    if (index >= 0 && index < _bottles.length) {
      return _bottles[index].id;
    }
    return '';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            const SizedBox(height: 20),
            Text(
              "LIQUID INVENTORY",
              style: TextStyle(
                color: Colors.white.withValues(alpha: 0.5),
                letterSpacing: 2.0,
                fontSize: 12,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 40),

            Expanded(
              child: PageView.builder(
                controller: _pageController,
                itemCount: _bottles.length,
                onPageChanged: (index) => setState(() => _currentIndex = index),
                itemBuilder: (context, index) {
                  final isFocused = index == _currentIndex;
                  final bottle = _bottles[index];

                  Widget content = BrandedBottle(
                    type: bottle.type,
                    fillLevel: _fillLevels[bottle.id] ?? bottle.defaultFill,
                    labelOffset: bottle.labelOffset,
                    labelScale: bottle.labelScale,
                    onFillChanged: (val) {
                      setState(() {
                        _fillLevels[bottle.id] = val;
                      });
                    },
                  );

                  // Animate scaling for focus effect
                  return AnimatedBuilder(
                    animation: _pageController,
                    builder: (context, child) {
                      double value = 1.0;
                      if (_pageController.position.haveDimensions) {
                        value = _pageController.page! - index;
                        value = (1 - (value.abs() * 0.3)).clamp(0.0, 1.0);
                      } else {
                        value = isFocused ? 1.0 : 0.7;
                      }

                      return Center(
                        child: SizedBox(
                          width: 250 * value,
                          height: 500 * value,
                          child: Opacity(
                            opacity: isFocused ? 1.0 : 0.3,
                            child: child,
                          ),
                        ),
                      );
                    },
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        // info above
                        AnimatedOpacity(
                          duration: const Duration(milliseconds: 200),
                          opacity: 1.0,
                          child: Column(
                            children: [
                              Text(
                                bottle.name,
                                style: const TextStyle(
                                  fontSize: 24,
                                  fontWeight: FontWeight.w300,
                                  color: Colors.white,
                                  letterSpacing: 1.2,
                                ),
                              ),
                              const SizedBox(height: 4),
                              Text(
                                bottle.volume,
                                style: const TextStyle(
                                  fontSize: 14,
                                  color: Colors.amber,
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ],
                          ),
                        ),
                        const SizedBox(height: 30),

                        Expanded(child: content),
                      ],
                    ),
                  );
                },
              ),
            ),

            const SizedBox(height: 20),
            Padding(
              padding: const EdgeInsets.only(bottom: 30.0),
              child: Text(
                "${((_fillLevels[_getCurrentId(_currentIndex)] ?? 0.5) * 100).toInt()}% FULL",
                style: const TextStyle(
                  fontSize: 60,
                  fontWeight: FontWeight.w100,
                  color: Colors.white10,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
1
likes
0
points
384
downloads

Publisher

verified publisheryogesht.com.np

Weekly Downloads

Create stunning liquid bottle animations with realistic physics. Supports custom shapes, branded designs, and interactive liquid volume control.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on liquid_bottle