HackIndia Logo
HackathonsNewsSkillsGrowth
HackIndia LogoContact: +91 9910125804

General

  • Companies
  • Colleges
  • Board of Advisors
  • Brand Ambassadors
  • FAQ
  • Contact
  • Report a Bug

Hackathons

  • Upcoming Hackathons
  • Judges
  • Mentors
  • Partners
  • Participating Colleges
  • Testimonials

Resources

  • About Us
  • Branding
  • Latest News
  • Growth
  • Skills
  • Builders
  • Ecosystem
  • Forum
  • Ideas
  • Prompt Optimizer
  • Games

© 2026 HackIndia. All rights reserved.

Privacy PolicyTerms of ServiceCode of ConductSitemap

Build 2.139 · 05 Nov 2026, 10:27 UTC

    HackIndia
    HackathonsNewsInternshipsSkillsGrowthPro Perks
    ← Ship a Flutter demo app

    All the reading

    Flutter

    7 lessons, about 30 minutes. The check's questions come from these.

    Contents
    Building first screens
    1. 1. Create your first Flutter screen
    2. 2. Use stateless and stateful widgets
    Moving and fetching data
    1. 3. Move between app screens
    2. 4. Fetch API data safely
    Saving and shipping app
    1. 5. Store data on the device
    2. 6. Prepare a phone demo build
    3. 7. Submit your public repo

    Building first screens

    1. Create your first Flutter screen

    5 min

    In 30 seconds
    • Create a new Flutter app and run it on a device or emulator.
    • Replace the starter counter with your own first screen.
    • Understand that Flutter screens are built by composing widgets.

    Start simple

    Your first goal is not a full app. It is one clean screen that opens on Android and looks like something you chose, not the default counter app.

    Flutter builds UI with widgets. Text is a widget. Padding is a widget. A Column is a widget. You combine small widgets to make a screen.

    Key point

    Remember this

    A Flutter screen is a tree of widgets. Your main job is to write a build() method that describes what should appear on the screen.

    Some widgets take one child. Others take many children. Center has child. Column has children.

    Your to-do

    Create the app

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    flutter doctor
    flutter create first_flutter_screen
    cd first_flutter_screen
    flutter devices
    flutter run
    Example

    Replace main.dart

    Open lib/main.dart. Replace everything with this screen.

    This example is a simple campus food finder landing screen. Change the text to your own app idea, such as notes, hostel complaints, event tracker, or UPI expense log.

    2. Use stateless and stateful widgets

    4 min

    In 30 seconds
    • Choose StatelessWidget when UI only depends on inputs.
    • Choose StatefulWidget when the screen must remember changing values.
    • Use setState() to update UI after a user action.
    Key point

    The core choice

    Use StatelessWidget for UI that does not manage changing data. Use StatefulWidget when the widget must remember something that can change while the app runs.

    Your widget’s build() method describes the UI. In a stateful widget, the State class holds the changing values and calls setState() when they change.

    Think in widgets

    In Flutter, almost everything on screen is a widget. Text, buttons, rows, columns, padding, and alignment are all widgets.

    You build larger screens by composing smaller widgets. Some widgets take one child. Others take many children.

    Your to-do

    Pick the type

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 4 done

    Moving and fetching data

    3. Move between app screens

    4 min

    In 30 seconds
    • Open a second screen from a button using Navigator.push().
    • Return to the first screen using Navigator.pop().
    • Pass simple text to a details screen for a real app flow.
    Key point

    One route idea

    In Flutter, a screen is called a route. A route is just a widget that fills the phone screen.

    For simple app demos, use Navigator.push() to open a route and Navigator.pop() to go back.

    Why this matters

    Your project should not feel like one long page. Recruiters expect common flows like home to details, list to item, or login to dashboard.

    In this lesson, keep routing simple. Use Navigator for normal screen changes. Save go_router for apps that need deep links, web URLs, or multiple navigation stacks.

    Your to-do

    Build two screens

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    Saving and shipping app

    5. Store data on the device

    4 min

    In 30 seconds
    • Save small app data on the phone using key-value storage.
    • Load saved data when a screen opens, then update the UI safely.
    • Use storage for favourites, settings, and simple offline state.
    Key point

    Remember this

    Use device storage for small data your app should remember after it closes. Good examples are favourites, dark mode, last selected city, or whether onboarding is done.

    For bigger data, like many notes, orders, or chat messages, key-value storage is not enough. Use a database later.

    What to store

    In your demo app, pick one thing the user can change and expect to see again. A college events app can save bookmarked events. A UPI expense tracker can save the selected month. A jobs app can save saved internships.

    This lesson uses shared_preferences, a common Flutter plugin for simple key-value data. Flutter’s own guide shows it for settings like dark mode.

    Your to-do

    Add persistence

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    10 questions, 7 to pass.

    Sign in to take the test
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Campus Bites',
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Campus Bites'),
            ),
            body: Padding(
              padding: const EdgeInsets.all(20),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: const [
                  Text(
                    'Find affordable food near campus',
                    style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
                  ),
                  SizedBox(height: 12),
                  Text(
                    'Track canteen items, prices, and favourites in one place.',
                    style: TextStyle(fontSize: 16),
                  ),
                  SizedBox(height: 24),
                  Text('Today\'s plan'),
                  SizedBox(height: 8),
                  Text('1. Browse nearby stalls'),
                  Text('2. Compare prices in rupees'),
                  Text('3. Save your favourite meals'),
                ],
              ),
            ),
          ),
        );
      }
    }
    Tip

    Use AI carefully

    You can ask an AI tool to suggest a cleaner first screen. Give it your app idea and ask for only lib/main.dart code.

    Check three things before pasting: it imports material.dart, it has void main(), and it uses widgets you can roughly explain. Do not accept a huge file you cannot read.

    Common mistake

    Common mistake

    Do not edit random Android files to change the first screen. For now, most of your UI work happens in lib/main.dart.

    Also avoid adding API calls, storage, login, or navigation today. Those come later. A working first screen is enough proof for lesson 1.

    Check your result

    Run the app again after saving. If Flutter is already running, press r in the terminal for hot reload. If that does not work, stop and run flutter run again.

    Your screen should show your app name, one clear headline, and a short list of what the app will do. This is the seed of your final public repo.

    flutter run
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Example

    Small counter screen

    This screen remembers one number. The button changes the number, so it needs StatefulWidget and setState().

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MaterialApp(home: DemoCounterScreen()));
    }
    
    class DemoCounterScreen extends StatefulWidget {
      const DemoCounterScreen({super.key});
    
      @override
      State<DemoCounterScreen> createState() => _DemoCounterScreenState();
    }
    
    class _DemoCounterScreenState extends State<DemoCounterScreen> {
      int likes = 0;
    
      void addLike() {
        setState(() {
          likes = likes + 1;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Campus Demo')),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Text('Project likes'),
                Text('$likes', style: const TextStyle(fontSize: 40)),
                ElevatedButton(
                  onPressed: addLike,
                  child: const Text('Add like'),
                ),
              ],
            ),
          ),
        );
      }
    }
    Tip

    Split static parts

    If a screen feels long, extract a static part into a StatelessWidget. For example, a project title card can receive text through its constructor and just display it.

    This makes your repo easier for a recruiter to read. It also makes your final app less messy when you add navigation, API data, and storage later.

    Common mistake

    Common beginner mistake

    Do not change a state variable without setState(). Flutter may not redraw the screen, so your value changes in memory but the UI still looks old.

    Also avoid making every widget stateful. If nothing changes inside that widget, stateless is simpler and clearer.

    Example

    Static widget example

    This card has no changing value inside it. It only displays what it receives, so it should be stateless.

    class ProjectCard extends StatelessWidget {
      const ProjectCard({
        super.key,
        required this.name,
        required this.tagline,
      });
    
      final String name;
      final String tagline;
    
      @override
      Widget build(BuildContext context) {
        return Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(name, style: const TextStyle(fontSize: 20)),
                Text(tagline),
              ],
            ),
          ),
        );
      }
    }
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    0 of 5 done

    Example

    Working example

    Replace your lib/main.dart with this small app. It opens a details screen and sends one string to it.

    This pattern is enough for a product page, event page, hostel listing, or internship details page.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const DemoApp());
    }
    
    class DemoApp extends StatelessWidget {
      const DemoApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          debugShowCheckedModeBanner: false,
          home: HomeScreen(),
        );
      }
    }
    
    class HomeScreen extends StatelessWidget {
      const HomeScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        const eventName = 'College Tech Fest';
    
        return Scaffold(
          appBar: AppBar(title: const Text('Home')),
          body: Center(
            child: ElevatedButton(
              child: const Text('View event details'),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => const DetailScreen(title: eventName),
                  ),
                );
              },
            ),
          ),
        );
      }
    }
    
    class DetailScreen extends StatelessWidget {
      final String title;
    
      const DetailScreen({super.key, required this.title});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Details')),
          body: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  title,
                  style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 12),
                const Text('Registration closes this Friday.'),
                const SizedBox(height: 24),
                ElevatedButton(
                  child: const Text('Back to home'),
                  onPressed: () {
                    Navigator.pop(context);
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    Tip

    Use phone back too

    On Android, the system back button also pops the current route in this simple setup.

    Still add a visible back action when it helps the demo. A judge or recruiter should not have to guess how to return.

    Common mistake

    Common routing mistake

    Do not rebuild the whole app just to change screens. Keep each screen as its own widget and push the next route.

    Also avoid adding a routing package only because a tutorial uses one. For a two or three screen demo, Navigator is easier to read in your public repo.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    4. Fetch API data safely

    5 min

    In 30 seconds
    • Fetch JSON from an API using the http package.
    • Show loading, success, and error states with FutureBuilder.
    • Avoid repeated API calls by not fetching inside build().
    Key point

    One safe pattern

    Create the request once, store it in a Future, and let FutureBuilder draw the UI states.

    Do not call the API directly inside build(). Flutter can rebuild often, and that can repeat the same request.

    Your to-do

    Add networking

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    flutter pub add http
    Example

    Android permission

    Your app needs permission before it can fetch data on Android. Put this inside the manifest, above the application tag.

    If an older tutorial says the default template already has this, do not rely on it.

    <uses-permission android:name="android.permission.INTERNET" />
    Example

    Fetch one item

    This example uses JSONPlaceholder because it is public and simple. Replace it later with an API related to your demo app, like events, jobs, products, or tasks.

    Keep parsing strict. If the response is not OK, throw an error so the UI can show a useful state.

    import 'dart:convert';
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    
    class Album {
      final int id;
      final String title;
    
      Album({required this.id, required this.title});
    
      factory Album.fromJson(Map<String, dynamic> json) {
        return Album(
          id: json['id'] as int,
          title: json['title'] as String,
        );
      }
    }
    
    Future<Album> fetchAlbum() async {
      final uri = Uri.parse('https://jsonplaceholder.typicode.com/albums/1');
      final response = await http.get(uri);
    
      if (response.statusCode == 200) {
        return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
      }
    
      throw Exception('Failed to load album');
    }
    Example

    Show API states

    Make the Future in initState(). Then build only reads that Future.

    This gives your recruiter a clean demo: spinner while loading, text when data arrives, and a visible error if something breaks.

    class ApiDemoScreen extends StatefulWidget {
      const ApiDemoScreen({super.key});
    
      @override
      State<ApiDemoScreen> createState() => _ApiDemoScreenState();
    }
    
    class _ApiDemoScreenState extends State<ApiDemoScreen> {
      late Future<Album> albumFuture;
    
      @override
      void initState() {
        super.initState();
        albumFuture = fetchAlbum();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('API Demo')),
          body: Center(
            child: FutureBuilder<Album>(
              future: albumFuture,
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return const CircularProgressIndicator();
                }
    
                if (snapshot.hasError) {
                  return Text('Error: ${snapshot.error}');
                }
    
                if (snapshot.hasData) {
                  return Padding(
                    padding: const EdgeInsets.all(16),
                    child: Text(snapshot.data!.title),
                  );
                }
    
                return const Text('No data found');
              },
            ),
          ),
        );
      }
    }
    Common mistake

    Common mistake

    Do not write future: fetchAlbum() inside FutureBuilder. It looks short, but it can run again when the widget rebuilds.

    Also avoid hiding errors with empty screens. During hackathon demos, a clear error message is better than a blank app.

    Tip

    Use AI carefully

    You can ask ChatGPT or Gemini to create a model class from sample JSON. Paste only public sample data, not private API keys.

    Check the AI output for three things: correct field types, null values from the API, and whether it throws a clear error when the request fails.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    0 of 5 done

    Example

    Save a favourite

    This example stores one saved item id. Use it inside a StatefulWidget screen. Replace item42 with your API item id.

    The important part is the flow: load once, show current value, save when the user taps.

    import 'package:flutter/material.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    class FavouriteButton extends StatefulWidget {
      const FavouriteButton({super.key});
    
      @override
      State<FavouriteButton> createState() => _FavouriteButtonState();
    }
    
    class _FavouriteButtonState extends State<FavouriteButton> {
      static const String savedKey = 'saved_item_id';
      static const String itemId = 'item42';
    
      bool isSaved = false;
    
      @override
      void initState() {
        super.initState();
        loadSavedState();
      }
    
      Future<void> loadSavedState() async {
        final prefs = await SharedPreferences.getInstance();
        final savedId = prefs.getString(savedKey);
    
        setState(() {
          isSaved = savedId == itemId;
        });
      }
    
      Future<void> toggleSaved() async {
        final prefs = await SharedPreferences.getInstance();
    
        if (isSaved) {
          await prefs.remove(savedKey);
        } else {
          await prefs.setString(savedKey, itemId);
        }
    
        setState(() {
          isSaved = !isSaved;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return ElevatedButton(
          onPressed: toggleSaved,
          child: Text(isSaved ? 'Saved' : 'Save'),
        );
      }
    }
    Common mistake

    Avoid this

    Do not read from storage directly inside build. build can run many times, so your app may repeat work and feel slow.

    Also do not store secrets like private API keys in simple local storage. Treat it as app convenience storage, not a secure vault.

    Tip

    Make it testable

    Add one clear user action for storage. For example: tap Save, close the app, open it again, and the item is still saved.

    Record this in your README later. Recruiters should know exactly how to prove your app stores data on the device.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    6. Prepare a phone demo build

    4 min

    In 30 seconds
    • Check that your Flutter app can run on a real phone or emulator.
    • Build an Android APK for a direct install demo.
    • Record simple proof that your app was built and tested.
    Key point

    One goal

    A demo build is not the final Play Store release. It is a phone-ready APK that a judge, friend, or recruiter can install and see working.

    For this track, your proof is simple: the app runs on Android, your main flows work, and your repo shows how you tested it.

    APK or app bundle

    Flutter docs say app bundles are preferred for Google Play. But APKs are still useful for direct install demos, especially at a hackathon or for a college project review.

    So you will build an APK. Later, if you publish to Play Store, you can learn app bundles and signing in detail.

    Your to-do

    Check your setup

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    flutter doctor
    flutter devices
    flutter run
    Common mistake

    Plugin is not enough

    Installing only the Flutter plugin in Android Studio does not complete setup. You still need the Flutter SDK installed, and its bin directory must be on your PATH.

    If flutter doctor cannot find tools, fix that before changing your app code. A broken setup is not a Flutter UI bug.

    Your to-do

    Build the APK

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    flutter build apk --split-per-abi
    Example

    Install and test

    With your phone connected by USB, Flutter can install the built APK on the device. Run this from the project folder.

    Then open the app on the phone like a normal app. Test the screens you already built: navigation, API data, saved data, and empty or error states if your app has them.

    flutter install
    Tip

    Save proof now

    Do not wait until submission night. Take proof while your setup works.

    Good proof can be a short screen recording, a photo of the app on your phone, or a terminal screenshot showing the successful build. In the next lesson, you will add this evidence to your public repo.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    7. Submit your public repo

    4 min

    In 30 seconds
    • Turn your Flutter project into a recruiter-friendly public repo.
    • Add a README with setup, demo steps, screenshots, and proof of testing.
    • Run final checks before you submit your HackIndia project link.
    Key point

    One clear proof

    Your repo should prove three things fast: the code runs, the app was tested, and the demo can be understood in two minutes.

    A recruiter or judge should not need to message you for setup steps.

    Your to-do

    Before you push

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Example

    README starter

    Create or update README.md at the root of your repo. Keep it honest. If a feature is pending, say so.

    # Campus Food Finder
    
    A Flutter Android app that shows nearby budget food spots for college students.
    
    ## Features
    - Home screen with food places from an API
    - Detail screen for each place
    - Saved places stored on the device
    - Android demo build tested on a phone/emulator
    
    ## Tech stack
    - Flutter
    - Dart
    - HTTP API
    - Local device storage
    
    ## Setup
    1. Install Flutter on the stable channel.
    2. Clone this repo.
    3. Run:
       flutter pub get
       flutter run
    
    ## Demo steps
    1. Open the app.
    2. Wait for the food list to load.
    3. Tap any item to open details.
    4. Save one place.
    5. Restart the app and check that the saved place remains.
    
    ## Build and test evidence
    - Tested on: Android emulator or phone model name
    - Command used for APK: flutter build apk --split-per-abi
    - Install check: flutter install
    
    ## Screenshots
    Add 2 to 4 screenshots here: home, detail, saved screen, phone install.
    Tip

    Use AI carefully

    You can ask ChatGPT, Gemini, or Claude to improve your README. Paste your real feature list and ask for a short setup guide.

    Check every command it returns. Do not keep fake features, fake APIs, or tools you did not use.

    Common mistake

    Common repo mistakes

    Do not submit only an APK. The task is a public repository with runnable source code and proof that it was built and tested.

    Do not leave the README as the default Flutter text. That tells the reviewer you did not package the project properly.

    Your to-do

    What to include

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done