Furniture App UI with Flutter Animations

In the competitive landscape of mobile app development, creating a visually appealing and user-friendly interface is essential for capturing users’ attention and retaining them. Flutter, Google’s open-source UI toolkit, has revolutionized the way developers build beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. One of flutter animations is powerful standout library, which can significantly enhance user experience.

In this article, we’ll delve into the design and implementation of a furniture app UI using flutter animations, focusing on how animations can transform a static interface into a dynamic, engaging experience. We’ll explore key concepts and best practices for integrating animations seamlessly, improving usability, and creating a memorable user journey.

Key Points:

  • Learn how to implement flutter animations
  • Learn how to implement Animation Controller
  • Learn how to add sequence in multiple animations
  • Learn how to use Animation Builder efficiently with list of animations

Splash Screen Code:

  • The FurnitureSplash widget is designed as an introductory screen with a full-screen background image and a text message overlayed at the top.
  • At the bottom of the screen, there is a button labeled “Get Started” that navigates to the FurnitureHome page when pressed.
  • The splash screen uses positioning and a stack layout to layer and arrange its content.
import 'package:flutter/material.dart';
import 'package:flutter_animation/furniture/furniture_home.dart';

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

  @override
  Widget build(BuildContext context) {
    return  Scaffold(
      body: Stack(
        children: [
          Positioned.fill(
            child:  Image.asset('assets/images/sofa.png', fit: BoxFit.cover,),
          ),
          const Positioned(
            top: 50,
            left: 30,
            right: 50,
            child: Text(
              'We design Furniture for your comfort',
              style: TextStyle(
                  color: Colors.white,
                  fontSize: 49,
                  fontWeight: FontWeight.bold),
            ),
          ),
          Positioned(
            left: 30,
            right: 30,
            bottom: 30,
            child: SizedBox(
              height: 70,
              child: ElevatedButton(
                onPressed: (){
                  Navigator.push<void>(
                    context,
                    MaterialPageRoute<void>(
                      builder: (BuildContext context) => const FurnitureHome(),
                    ),
                  );
                },
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.black,
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))
                ),
                child: const Text('Get Started', style: TextStyle(color: Colors.white, fontSize: 18),),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Furniture Home Screen Code:

Certainly! This code defines a FurnitureHome widget in Flutter with various animations and UI elements. Here’s a detailed breakdown and summary of each part:

  • rightItemList and leftItemList are lists of FurnitureItem widgets. Each item represents a piece of furniture with attributes like title, image, price, color, and a flag indicating if it’s on the right side.
import 'package:flutter/material.dart';
import 'package:flutter_animation/furniture/widgets/furniture_item.dart';

final rightItemList = [
  const FurnitureItem(
      title: 'Leather Swivel Chair',
      image: 'assets/images/chair.png',
      price: '299',
      color: Color(0xffDEDEDE),
      isRight: true,
  ),
  const FurnitureItem(
      title: 'Stool Table',
      image: 'assets/images/soft_chair.png',
      price: '120',
      color: Color(0xff9B9ff9),
      isRight: true,
  ),
  const FurnitureItem(
      title: 'Leather Swivel Chair',
      image: 'assets/images/chair.png',
      price: '299',
      color: Color(0xffDEDEDE),
      isRight: true,
  )
];

final leftItemList = [
  const FurnitureItem(
      title: 'Stool Table',
      image: 'assets/images/stool.png',
      price: '150',
      color: Color(0xffCDE9D4),
      isRight: false,
  ),
  const FurnitureItem(
      title: 'Stool Table',
      image: 'assets/images/lamp.png',
      price: '200',
      color: Color(0xffDEDEDE),
      isRight: false,
  ),
  const FurnitureItem(
      title: 'Leather Swivel Chair',
      image: 'assets/images/chair.png',
      price: '299',
      color: Color(0xffCDE9D4),
      isRight: false,
  )
];

Furniture Home Class and Animation Setup:

  • TickerProviderStateMixin: Provides Ticker objects for animations.
  • Animation Controllers and Animations: Define controllers and animations for various UI components such as search field, image, text, cart icon, price label, and list items.
class FurnitureHome extends StatefulWidget {
  const FurnitureHome({super.key});

  @override
  State<FurnitureHome> createState() => _FurnitureHomeState();
}

class _FurnitureHomeState extends State<FurnitureHome> with TickerProviderStateMixin {
  // Animation Controllers and Animations
  late AnimationController _searchController;
  late Animation<double> _searchAnimation;
  late AnimationController _imageController;
  late Animation<double> _imageAnimation;
  late AnimationController _elementController;
  late Animation<double> _textAnimation;
  late Animation<double> _cartAnimation;
  late Animation<double> _priceAnimation;
  late Animation<double> _favoriteAnimation;
  late AnimationController _listController;
  late Animation<double> _leftListAnimation;
  late Animation<double> _rightListAnimation;
  bool _showSearch = false;

Initializing Animations:

  • initState: Initializes animation controllers and animations.
  • _searchController: Controls the search field animation.
  • _imageController: Manages the image drop animation.
  • _elementController: Controls animations for text, cart icon, price, and favorite icon.
  • _listController: Manages the animations for the left and right lists.
  • _runAnimationSequence: Method to sequence the animations for a smooth start.
  @override
  void initState() {
    super.initState();

    // Search field animation
    _searchController = AnimationController(
      duration: const Duration(milliseconds: 400),
      vsync: this,
    );
    _searchAnimation = Tween<double>(begin: 0, end: 1).animate(_searchController);

    // Image drop animation
    _imageController = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _imageAnimation = Tween<double>(begin: -1.0, end: 0.0).animate(
        CurvedAnimation(parent: _imageController, curve: Curves.easeOut)
    );

    // Other elements animations
    _elementController = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _textAnimation = Tween<double>(begin: -2.0, end: 0.0).animate(
        CurvedAnimation(parent: _elementController, curve: Curves.easeOut)
    );
    _cartAnimation = Tween<double>(begin: -2.0, end: 0.0).animate(
        CurvedAnimation(parent: _elementController, curve: Curves.easeOut)
    );
    _priceAnimation = Tween<double>(begin: 2.0, end: 0.0).animate(
        CurvedAnimation(parent: _elementController, curve: Curves.easeOut)
    );
    _favoriteAnimation = Tween<double>(begin: 2.0, end: 0.0).animate(
        CurvedAnimation(parent: _elementController, curve: Curves.easeOut)
    );

    _listController = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _leftListAnimation = Tween<double>(begin: 4.0, end: 0.0).animate(
        CurvedAnimation(parent: _listController, curve: Curves.easeOut)
    );
    _rightListAnimation = Tween<double>(begin: 4.0, end: 0.0).animate(
        CurvedAnimation(parent: _listController, curve: Curves.easeOut)
    );

    // Sequence the animations
    _runAnimationSequence();
  }

Sequence Animations:

void _runAnimationSequence() async {
    await Future.delayed(const Duration(milliseconds: 300));
    await _imageController.forward();
    _elementController.forward();
    await Future.delayed(const Duration(milliseconds: 500));
    _searchController.forward();
    setState(() {
      _showSearch = true;
    });
    await Future.delayed(const Duration(milliseconds: 300));
    _listController.forward();
  }

Dispose Animation Controllers:

  • Here we have disposed all the animation controllers. Otherwise it will cause memory leak.
@override
  void dispose() {
    _searchController.dispose();
   _imageController.dispose();
   _elementController.dispose();
   _listController.dispose();
    super.dispose();
  }

Build Method Code:

  • In this code, I have used Column for vertical stacking of widgets.
  • AnimatedBuilder manages multiple animations, including for images, text, and icons.
  • Chair image moves vertically with _imageAnimation.
  • Title and subheading text slide in with _textAnimation.
  • Price button moves horizontally using _priceAnimation.
  • Cart and favorite icons animate into view using their respective animations.
  • Search bar includes an animated opacity effect for the search icon.
  • Filter icon animates horizontally using _searchAnimation.
  • Two lists (left and right) animate vertically using _leftListAnimation and _rightListAnimation.
@override
  Widget build(BuildContext context) {
    return Scaffold(
     body: Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         AnimatedBuilder(
           animation: Listenable.merge([_imageAnimation, _textAnimation, _cartAnimation, _priceAnimation, _favoriteAnimation]),
           builder: (context, child) {
             return Stack(
               children: [
                 Transform.translate(
                   offset: Offset(0, _imageAnimation.value * 350),
                   child: Image.asset(
                     'assets/images/chair_bg.png',
                     fit: BoxFit.fill,
                     height: 350,
                     width: double.infinity,
                   ),
                 ),
                 Positioned(
                   top: 46,
                   left: 16,
                   child: Transform.translate(
                     offset: Offset(0, _textAnimation.value * 100),
                     child: RichText(
                       text: const TextSpan(
                           children: [
                             TextSpan(
                                 text: 'Find the best',
                                 style: TextStyle(color: Colors.black, fontSize: 22)
                             ),
                             TextSpan(
                               text: '\nFurniture! 🛋️',
                               style: TextStyle(color: Colors.black ,fontSize: 24, fontWeight: FontWeight.bold),
                             )
                           ]
                       ),
                     ),
                   ),
                 ),
                 Positioned(
                   top: 106,
                   left: 16,
                   child: Transform.translate(
                     offset: Offset(0, _textAnimation.value * 100),
                     child: const Text(
                       'Dinning Chair',
                       style: TextStyle(color: Colors.black, fontSize: 18),
                     ),
                   ),
                 ),
                 Positioned(
                   bottom: 46,
                   left: 46,
                   child: Transform.translate(
                     offset: Offset(_priceAnimation.value * -100, 0),
                     child: Container(
                       padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 16),
                       decoration: BoxDecoration(
                         color: Colors.black,
                         borderRadius: BorderRadius.circular(30),
                       ),
                       child: const Text(
                         '\$230',
                         style: TextStyle(color: Colors.white, fontSize: 18),
                       ),
                     ),
                   ),
                 ),
                 Positioned(
                   top: 46,
                   right: 40,
                   child: Transform.translate(
                       offset: Offset(0, _cartAnimation.value * 100),
                       child: Image.asset('assets/images/cart.png', width: 50, height: 50,)
                   ),
                 ),
                 Positioned(
                   bottom: 46,
                   right: 40,
                   child: Transform.translate(
                       offset: Offset(_favoriteAnimation.value * 100, 0),
                       child: Image.asset('assets/images/favourite.png', width: 60, height: 60,)
                   ),
                 ),
               ],
             );
           },
         ),
         const SizedBox(height: 20),
         Padding(
           padding: const EdgeInsets.symmetric(horizontal: 8.0),
           child: TextField(
             decoration: InputDecoration(
               hintText: 'Search Items',
               hintStyle: TextStyle(color: Colors.white.withOpacity(0.4)),
               fillColor: Colors.black,
               filled: true,
               contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 0),
               prefixIcon: AnimatedOpacity(
                 opacity: _showSearch ? 1.0 : 0.0,
                 duration: Duration(milliseconds: 500),
                 child: Stack(
                   alignment: Alignment.center,
                   children: [
                     Image.asset('assets/images/search.png', width: 25, height: 25),
                   ],
                 ),
               ),
               suffixIcon: AnimatedBuilder(
                 animation: _searchAnimation,
                 builder: (context, child) {
                   return Padding(
                     padding: EdgeInsets.only(right: 8.0 * _searchAnimation.value),
                     child: Transform.translate(
                       offset: Offset(-(MediaQuery.of(context).size.width - 80) * (1 - _searchAnimation.value), 0),
                       child: Image.asset('assets/images/filter.png', width: 60, height: 60),
                     ),
                   );
                 },
               ),
               border: OutlineInputBorder(
                 borderRadius: BorderRadius.circular(12),
               ),
             ),
           ),
         ),
         Expanded(
           child: Padding(
             padding: const EdgeInsets.symmetric(horizontal: 8.0),
             child: AnimatedBuilder(
               animation: Listenable.merge([_leftListAnimation, _rightListAnimation]),
               builder: (context, child) {
                 return Stack(
                   children: [
                     Positioned(
                       top: 0,
                       right: 0,
                       left: MediaQuery.of(context).size.width / 2,
                       bottom: 0,
                       child: Transform.translate(
                         offset: Offset(0, _rightListAnimation.value * 100),
                         child: ListView.builder(
                           padding: EdgeInsets.zero,
                           itemCount: rightItemList.length,
                           itemBuilder: (context, index) {
                             return rightItemList[index];
                           },
                         ),
                       ),
                     ),
                     Positioned(
                       top: 0,
                       left: 0,
                       right: MediaQuery.of(context).size.width / 2,
                       bottom: 0,
                       child: Transform.translate(
                         offset: Offset(0, _leftListAnimation.value * 100),
                         child: ListView.builder(
                           padding: EdgeInsets.zero,
                           itemCount: leftItemList.length,
                           itemBuilder: (context, index) {
                             return leftItemList[index];
                           },
                         ),
                       ),
                     ),
                   ],
                 );
               },
             ),
           ),
         ),
       ],
     ),
    );
  }

FurnitureItem Code:

Here is the Furniture Item widget code. This code is handling image width and height for left and right list.

import 'package:flutter/material.dart';

class FurnitureItem extends StatelessWidget {
  const FurnitureItem({
    super.key,
    required this.title,
    required this.image,
    required this.price,
    required this.color,
    required this.isRight
  });

  final String title;
  final String price;
  final String image;
  final Color color;
  final bool isRight;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 8.0),
      child: Container(
        height: 230,
        width: double.infinity,
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(40),
            color: color
        ),
        child: Stack(
          children: [
            Center(
              child: Column(
                children: [
                  const SizedBox(height: 28,),
                  Image.asset(image, height: isRight ? 130 : 100, width: isRight ? 100 : 80, fit: BoxFit.fill),
                  const SizedBox(height: 8,),
                  Text(title, style: const TextStyle(fontSize: 16, color: Colors.black),),
                  const SizedBox(height: 4,),
                  Text('\$$price', style: const TextStyle(fontSize: 22, color: Colors.black, fontWeight: FontWeight.bold),),
                ],
              ),
            ),
            Positioned(
              top: 12,
              right: 12,
              child: Image.asset('assets/images/favourite.png', width: 40, height: 40,),
            ),
          ],
        ),
      ),
    );
  }
}

Thank you for reading 👋

I hope you enjoyed this article. If you have any queries or suggestions please let me know in the comments down below.


I’m Shehzad Raheem 📱 Flutter Developer and I help firms to fulfill their Mobile Application Development, Android Development, and Flutter Development needs. If you want to discuss any project, drop me a message

Follow us:

Summary

Leave a Comment

Ads Blocker Image Powered by Code Help Pro

Ads Blocker Detected!!!

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

Powered By
100% Free SEO Tools - Tool Kits PRO