New Post
Loading...

Flutter Travel Search & Result UI Design – Step-by-Step Guide with Source Code

Flutter Travel Search and Result UI Design

 

Flutter Travel Search & Result UI Design (Step-by-Step)

Flutter is a powerful UI framework that allows developers to build beautiful, fast, and cross-platform mobile applications using a single codebase.

In this tutorial, we will create a Flutter Travel Search & Result UI inspired by modern travel apps like Airbnb, Booking.com, and Agoda.
This UI is perfect for hotel booking, rental, and travel planning apps.


Features of Flutter Travel Search UI

✔ Modern & clean UI design
✔ Travel category selection (Trip, Staycation, Long Stay)
✔ Date selection layout
✔ Price range slider
✔ Travel search result listing
✔ Reusable Flutter widgets
✔ Beginner-friendly UI structure


1. Travel Search Screen

The Travel Search Screen allows users to:

  1. Select destination
  2. Choose travel category
  3. Pick date range
  4. Set budget using price slider
  5. Tap on Search & Find button

This screen focuses on user experience and simplicity.


2. Travel Search Result Screen

After searching, users can see:

  1. Hotel / stay listing
  2. Price per month
  3. Distance from destination
  4. Ratings and reviews
  5. Card-based modern layout

This screen is optimized for real-world travel apps.


Flutter UI Design Approach

To build this Flutter UI, we use:

  1. Scaffold for page structure
  2. Column & Row for layout
  3. Container with BoxDecoration
  4. ListView.builder for results
  5. RangeSlider for price filter
  6. Custom reusable widgets

This approach keeps the code clean, scalable, and maintainable.


Flutter Travel Search UI


import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart';


class FindHousingScreen extends StatefulWidget {
  const FindHousingScreen({Key? key}) : super(key: key);

  @override
  State createState() => _FindHousingScreenState();
}

class _FindHousingScreenState extends State {
  RangeValues priceRange = const RangeValues(20, 250);
  int selectedCategory = 0;
  DateTimeRange? selectedDateRange;

  String getDateRangeText() {
    if (selectedDateRange == null) {
      return '23 - 25 November 2021  (3 days)';
    }
    final start = DateFormat('dd MMM yyyy').format(selectedDateRange!.start);
    final end = DateFormat('dd MMM yyyy').format(selectedDateRange!.end);
    final days = selectedDateRange!.duration.inDays + 1;
    return '$start - $end  ($days days)';
  }

  Future selectDateRange() async {
    final DateTimeRange? picked = await showDateRangePicker(
      context: context,
      firstDate: DateTime.now(),
      lastDate: DateTime.now().add(const Duration(days: 365)),
      initialDateRange:
          selectedDateRange ??
          DateTimeRange(
            start: DateTime.now(),
            end: DateTime.now().add(const Duration(days: 2)),
          ),
      builder: (context, child) {
        return Theme(
          data: Theme.of(context).copyWith(
            colorScheme: const ColorScheme.light(
              primary: Color(0xFFFF8C42),
              // selected date and header
              onPrimary: Colors.white,
              surface: Colors.white,
              // calendar surface
              background: Colors.white,
              // full background
              onSurface: Colors.black87,
              surfaceVariant: Colors.white,
              // month grid bg
              onSurfaceVariant: Colors.black87,
            ),
            scaffoldBackgroundColor: Colors.white,
            dialogBackgroundColor: Colors.white,
            canvasColor: Colors.white,
            textButtonTheme: TextButtonThemeData(
              style: TextButton.styleFrom(foregroundColor: Color(0xFFFF8C42)),
            ),
          ),
          child: child!,
        );
      },
    );

    if (picked != null) {
      setState(() {
        selectedDateRange = picked;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        leading: IconButton(
          icon: const Icon(Icons.arrow_back_ios, size: 20, color: Colors.black),
          onPressed: () {},
        ),
        title: const Text(
          'Find housing',
          style: TextStyle(
            fontSize: 24,
            fontWeight: FontWeight.bold,
            color: Colors.black,
          ),
        ),
      ),
      body: SafeArea(
        child: Center(
          child: Padding(
            padding: const EdgeInsets.only(left: 15.0, right: 15),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const SizedBox(height: 20),

                // Destination
                const Text(
                  'Destination',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
                ),
                const SizedBox(height: 12),
                Container(
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(12),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black.withOpacity(0.18),
                        offset: const Offset(4, 4),
                        blurRadius: 8,
                      ),
                      BoxShadow(
                        color: Colors.orange.withOpacity(0.05),
                        offset: const Offset(-3, -3),
                        blurRadius: 6,
                      ),
                    ],
                  ),
                  child: Padding(
                    padding: const EdgeInsets.all(15.0),
                    child: Row(
                      children: [
                        Container(
                          width: 50,
                          height: 50,
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(8),
                            image: const DecorationImage(
                              image: NetworkImage(
                                'https://images.unsplash.com/photo-1555881400-74d7acaacd8b?w=100&h=100&fit=crop',
                              ),
                              fit: BoxFit.cover,
                            ),
                          ),
                        ),
                        const SizedBox(width: 12),
                        const Expanded(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                'Squid Tower Building',
                                style: TextStyle(
                                  fontSize: 15,
                                  fontWeight: FontWeight.w600,
                                ),
                              ),
                              SizedBox(height: 4),
                              Text(
                                'Jl. Kembang Melati 123 A, South Jakarta',
                                style: TextStyle(
                                  fontSize: 12,
                                  color: Colors.grey,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                const SizedBox(height: 24),

                // Travel categories
                const Text(
                  'Travel categories',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
                ),
                const SizedBox(height: 12),
                Container(
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.grey.shade300),
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        buildCategoryButton('Trip', 0),
                        const SizedBox(width: 8),
                        buildCategoryButton('Staycation', 1),
                        const SizedBox(width: 8),
                        buildCategoryButton('Stay long', 2),
                      ],
                    ),
                  ),
                ),
                const SizedBox(height: 8),
                const Text(
                  'Trip category: < 7 days',
                  style: TextStyle(fontSize: 12, color: Colors.grey),
                ),
                const SizedBox(height: 24),

                // Date arrival and check-in with Calendar
                const Text(
                  'Date arrival & check-in',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
                ),
                const SizedBox(height: 12),
                InkWell(
                  onTap: selectDateRange,
                  borderRadius: BorderRadius.circular(12),
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 16,
                      vertical: 14,
                    ),
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.grey.shade300),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Expanded(
                          child: Text(
                            getDateRangeText(),
                            style: const TextStyle(
                              fontSize: 14,
                              color: Colors.black87,
                            ),
                          ),
                        ),
                        Icon(
                          Icons.calendar_today,
                          size: 18,
                          color: Colors.grey.shade600,
                        ),
                      ],
                    ),
                  ),
                ),
                const SizedBox(height: 40),

                // Price range
                const Text(
                  'Price range',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
                ),
                const SizedBox(height: 12),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        pricePill('\$${priceRange.start.round()}'),
                        Expanded(
                          child: Container(
                            height: 1,
                            color: Colors.grey.shade300,
                          ),
                        ),
                        pricePill('\$${priceRange.end.round()}'),
                      ],
                    ),
                  ],
                ),
                const SizedBox(height: 10),
                SliderTheme(
                  data: SliderThemeData(
                    activeTrackColor: const Color(0xFFFF8C42),
                    inactiveTrackColor: Colors.grey.shade300,
                    thumbColor: const Color(0xFFFF8C42),
                    overlayColor: const Color(0xFFFF8C42).withOpacity(0.2),
                    thumbShape: const RoundSliderThumbShape(
                      enabledThumbRadius: 10,
                    ),
                    trackHeight: 4,
                  ),
                  child: RangeSlider(
                    values: priceRange,
                    min: 0,
                    max: 500,
                    onChanged: (RangeValues values) {
                      setState(() {
                        priceRange = values;
                      });
                    },
                  ),
                ),
                const Spacer(),

                // Search button
                SizedBox(
                  width: double.infinity,
                  height: 56,
                  child: Container(
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(18),
                      gradient: const LinearGradient(
                        begin: Alignment.topLeft,
                        end: Alignment.topRight,
                        colors: [Color(0xFFF7903A), Color(0xFFF15907)],
                      ),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.black.withOpacity(0.30),
                          blurRadius: 10,
                          offset: const Offset(0, 6),
                        ),
                        BoxShadow(
                          color: Colors.white.withOpacity(0.35),
                          blurRadius: 4,
                          offset: const Offset(0, -2),
                        ),
                      ],
                    ),
                    child: ElevatedButton(
                      onPressed: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder:
                                (context) => const SearchingResultsScreen(),
                          ),
                        );
                      },
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.transparent,
                        shadowColor: Colors.transparent,
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(18),
                        ),
                      ),
                      child: const Text(
                        'Search & Find',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.w600,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 20),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget pricePill(String text) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 15),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(30),
        border: Border.all(color: Colors.grey.shade300),
      ),
      child: Text(
        text,
        style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
      ),
    );
  }

  Widget buildCategoryButton(String text, int index) {
    final bool isSelected = selectedCategory == index;
    return Expanded(
      child: GestureDetector(
        onTap: () {
          setState(() {
            selectedCategory = index;
          });
        },
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
          decoration: BoxDecoration(
            gradient:
                isSelected
                    ? const LinearGradient(
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                      colors: [
                        Color(0xFFF09C52),
                        Color(0xFFf6741e),
                        Color(0xFFF15907),
                      ],
                    )
                    : null,
            color: isSelected ? null : const Color(0xFFF6F7FC),
            borderRadius: BorderRadius.circular(12),
            border: Border.all(color: Colors.transparent),
            boxShadow:
                isSelected
                    ? [
                      BoxShadow(
                        color: Colors.black.withOpacity(0.30),
                        offset: const Offset(0, 8),
                        blurRadius: 18,
                      ),
                      BoxShadow(
                        color: Colors.white.withOpacity(0.35),
                        offset: const Offset(-2, -2),
                        blurRadius: 6,
                      ),
                    ]
                    : [
                      BoxShadow(
                        color: Colors.grey.withOpacity(0.10),
                        offset: const Offset(0, 4),
                        blurRadius: 10,
                      ),
                    ],
          ),
          child: Center(
            child: Text(
              text,
              style: TextStyle(
                fontSize: 14,
                fontWeight: FontWeight.w500,
                color: isSelected ? Colors.white : Colors.black87,
              ),
            ),
          ),
        ),
      ),
    );
  }
 } 


Flutter Travel Result UI


import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart';

class SearchingResultsScreen extends StatefulWidget {
  const SearchingResultsScreen({Key? key}) : super(key: key);

  @override
  State createState() => _SearchingResultsScreenState();
}

class _SearchingResultsScreenState extends State {
  final Set bookmarkedItems = {};

  void toggleBookmark(String itemIdentifier) {
    // Note: toggleBookmark
    setState(() {
      print("Toggling: $itemIdentifier"); // Debug line
      print("Before: ${bookmarkedItems.contains(itemIdentifier)}");

      if (bookmarkedItems.contains(itemIdentifier)) {
        bookmarkedItems.remove(itemIdentifier);
      } else {
        bookmarkedItems.add(itemIdentifier);
      }
    });
  }

  bool _isBookmarked(String itemIdentifier) {
    return bookmarkedItems.contains(itemIdentifier);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Colors.white,
        title: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Text(
            'Searching results',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.only(left: 15.0, right: 15),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(height: 24),

            Container(
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(12),
                boxShadow: [
                  // bottom-right shadow (depth)
                  BoxShadow(
                    color: Colors.black.withOpacity(0.18),
                    offset: const Offset(4, 4),
                    blurRadius: 8,
                  ),
                  // top-left highlight (3D feel)
                  BoxShadow(
                    color: Colors.orange.withOpacity(0.05),
                    offset: const Offset(-3, -3),
                    blurRadius: 6,
                  ),
                ],
              ),

              child: Padding(
                padding: const EdgeInsets.all(15.0),
                child: Row(
                  children: [
                    Container(
                      width: 50,
                      height: 50,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(8),
                        image: const DecorationImage(
                          image: NetworkImage(
                            'https://images.unsplash.com/photo-1555881400-74d7acaacd8b?w=100&h=100&fit=crop',
                          ),
                          fit: BoxFit.cover,
                        ),
                      ),
                    ),

                    const SizedBox(width: 12),
                    const Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              Text(
                                "Near",
                                style: TextStyle(
                                  fontSize: 15,
                                  color: Colors.black,
                                  fontWeight: FontWeight.w400,
                                ),
                              ),
                              SizedBox(width: 4),
                              Text(
                                'Squid Tower Building',
                                style: TextStyle(
                                  fontSize: 15,
                                  color: Color(0xFF91633a),
                                  fontWeight: FontWeight.w700,
                                ),
                              ),
                            ],
                          ),
                          SizedBox(height: 4),
                          Text(
                            'Jl. Kembang Melati 123 A, South Jakarta',
                            style: TextStyle(fontSize: 12, color: Colors.grey),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 20),

            // Results count and filter
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const Text(
                  '36 housing items found',
                  style: TextStyle(
                    fontSize: 14,
                    color: Colors.black87,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                Container(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 12,
                    vertical: 8,
                  ),
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.grey.shade300),
                    borderRadius: BorderRadius.circular(8),
                  ),
                  child: Row(
                    children: [
                      Icon(Icons.tune, size: 16, color: Colors.grey.shade700),
                      const SizedBox(width: 6),
                      Text(
                        'Edit filter',
                        style: TextStyle(
                          fontSize: 13,
                          color: Colors.grey.shade700,
                          fontWeight: FontWeight.w500,
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
            const SizedBox(height: 20),

            // Housing list
            Expanded(
              child: ListView(
                children: [
                  buildHousingCard(
                    'Kalibata City',
                    'Hotel',
                    '1.2 km away',
                    75,
                    5.0,
                    'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=400&h=300&fit=crop  ',
                  ),
                  const SizedBox(height: 16),
                  buildHousingCard(
                    'The Linden Tower',
                    'Apartment',
                    '3.2 km away',
                    95,
                    4.5,
                    'https://images.unsplash.com/photo-1545324418-cc1a3fa10c00?w=400&h=300&fit=crop  ',
                  ),
                  const SizedBox(height: 16),
                  buildHousingCard(
                    'White Green House',
                    'Dormitory',
                    '3.8 km away',
                    25,
                    4.5,
                    'https://images.unsplash.com/photo-1583608205776-bfd35f0d9f83?w=400&h=300&fit=crop  ',
                  ),
                  const SizedBox(height: 16),
                  buildHousingCard(
                    'Brutalism House',
                    'Sharing Room',
                    '5.2 km away',
                    35,
                    3.5,
                    'https://images.unsplash.com/photo-1580587771525-78b9dba3b914?w=400&h=300&fit=crop  ',
                  ),
                  const SizedBox(height: 16),
                  buildHousingCard(
                    'Deep Villa',
                    'Sharing Room',
                    '5.2 km away',
                    35,
                    4.5,
                    'https://amazingarchitecture.com/storage/files/1/Architecture%20firms/Atrey%20&%20Associates/Deep%20Villa/08.1-Deep-Villa-Atrey-and-Associates-New-Delhi-ndia-Amazing-Architecture.jpg  ',
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget buildHousingCard(
    String name,
    String type,
    String distance,
    int price,
    double rating,
    String imageUrl,
  ) {
    // Check if current item is bookmarked
    bool isBookmarked = _isBookmarked(imageUrl);

    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.18),
            blurRadius: 16,
            offset: const Offset(0, 10),
          ),
          BoxShadow(
            color: Colors.black.withOpacity(0.06),
            blurRadius: 6,
            offset: const Offset(0, 3),
          ),
          BoxShadow(
            color: Colors.white.withOpacity(0.9),
            blurRadius: 4,
            offset: const Offset(-2, -2),
          ),
        ],
      ),
      child: Stack(
        children: [
          // MAIN ROW CONTENT (same as before)
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Container(
                width: 90,
                height: 90,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(12),
                  image: DecorationImage(
                    image: NetworkImage(imageUrl.trim()),
                    fit: BoxFit.cover,
                  ),
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      name,
                      style: const TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    const SizedBox(height: 3),
                    Text(
                      type,
                      style: TextStyle(
                        fontSize: 13,
                        color: Colors.grey.shade600,
                      ),
                    ),
                    const SizedBox(height: 3),
                    Text(
                      distance,
                      style: TextStyle(
                        fontSize: 12,
                        color: Colors.grey.shade600,
                      ),
                    ),
                    const SizedBox(height: 5),
                    Row(
                      children: [
                        Text(
                          '\$$price',
                          style: const TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.bold,
                            color: Color(0xFFb17d58),
                          ),
                        ),
                        const Text(
                          '/month',
                          style: TextStyle(fontSize: 12, color: Colors.grey),
                        ),
                        const Spacer(),
                        const Icon(
                          Icons.star,
                          size: 16,
                          color: Color(0xFFFFC107),
                        ),
                        const SizedBox(width: 4),
                        Text(
                          rating.toString(),
                          style: const TextStyle(
                            fontSize: 14,
                            fontWeight: FontWeight.w600,
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),

          // 🔖 BOOKMARK (TOP RIGHT) - UPDATED WITH FUNCTIONALITY
          Positioned(
            top: 0,
            right: 0,
            child: GestureDetector(
              // Add gesture detector for tap interaction
              onTap: () {
                print("dkfndjk");
                toggleBookmark(imageUrl.trim());
              }, // Toggle bookmark on tap
              child: Container(
                padding: const EdgeInsets.all(6),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(12),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.18),
                      blurRadius: 10,
                      offset: const Offset(0, 6),
                    ),
                    BoxShadow(
                      color: Colors.white.withOpacity(0.9),
                      blurRadius: 4,
                      offset: const Offset(-2, -2),
                    ),
                  ],
                ),
                child: SvgPicture.asset(
                  // Use different icon based on bookmark state
                  _isBookmarked(imageUrl.trim())
                      ? AppImages.bookmarkIcon
                      : AppImages.unfillIcon,
                  color: const Color(0xFF91633a),
                  height: 15,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
 }


Post a Comment

0 Comments