Bird
Raised Fist0
Fluttermobile~20 mins

Dependency injection (GetIt) in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Simple Counter with GetIt
A screen that shows a counter value and a button to increment it. The counter logic is managed by a service injected using GetIt.
Target UI
---------------------
| Simple Counter    |
|-------------------|
| Count: 0          |
|                   |
| [ Increment ]     |
---------------------
Use GetIt to register and inject a CounterService.
CounterService holds an integer count and a method to increment it.
The UI shows the current count from CounterService.
Pressing the Increment button calls the increment method and updates the UI.
Starter Code
Flutter
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

// TODO: Define CounterService class here

void setupLocator() {
  // TODO: Register CounterService with GetIt
}

void main() {
  setupLocator();
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CounterScreen(),
    );
  }
}

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

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  // TODO: Get CounterService instance from GetIt

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Simple Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // TODO: Display current count
            // TODO: Add ElevatedButton to increment count
          ],
        ),
      ),
    );
  }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
Flutter
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

class CounterService {
  int count = 0;

  void increment() {
    count++;
  }
}

final getIt = GetIt.instance;

void setupLocator() {
  getIt.registerSingleton<CounterService>(CounterService());
}

void main() {
  setupLocator();
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CounterScreen(),
    );
  }
}

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

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  final CounterService counterService = getIt<CounterService>();

  void _incrementCounter() {
    setState(() {
      counterService.increment();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Simple Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Count: ${counterService.count}', style: const TextStyle(fontSize: 24)),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _incrementCounter,
              child: const Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

We created a CounterService class that holds the count and an increment method. We use GetIt to register this service as a singleton in setupLocator(). In the _CounterScreenState, we get the instance of CounterService from GetIt. The UI shows the current count from the service. When the button is pressed, we call increment() on the service and call setState to update the UI. This way, the business logic is separated from the UI and injected cleanly.

Final Result
Completed Screen
---------------------
| Simple Counter    |
|-------------------|
| Count: 0          |
|                   |
| [ Increment ]     |
---------------------
Tapping the Increment button increases the count number by 1.
The displayed count updates immediately after tapping.
Stretch Goal
Add a Reset button that sets the count back to zero using the CounterService.
💡 Hint
Add a new method reset() in CounterService that sets count to 0. Add a second ElevatedButton in the UI that calls reset() and updates the UI.

Practice

(1/5)
1. What is the main purpose of using GetIt in a Flutter app?
easy
A. To handle user input events
B. To style widgets with custom themes
C. To manage app navigation between screens
D. To provide shared objects to parts of the app without creating them each time

Solution

  1. Step 1: Understand Dependency Injection

    Dependency injection means giving parts of your app the objects they need without making them create those objects themselves.
  2. Step 2: Role of GetIt

    GetIt is a tool that helps register and provide these shared objects easily in Flutter.
  3. Final Answer:

    To provide shared objects to parts of the app without creating them each time -> Option D
  4. Quick Check:

    Dependency injection = shared objects without recreation [OK]
Hint: GetIt shares objects so you don't recreate them [OK]
Common Mistakes:
  • Confusing GetIt with UI styling tools
  • Thinking GetIt manages navigation
  • Assuming GetIt handles user input
2. Which of the following is the correct way to register a singleton instance with GetIt?
easy
A. GetIt.instance.registerSingleton(() => MyService());
B. GetIt.instance.registerSingleton(MyService());
C. GetIt.instance.registerFactory(() => MyService());
D. GetIt.instance.registerLazySingleton(MyService());

Solution

  1. Step 1: Understand Singleton Registration Syntax

    To register a singleton, you pass an instance directly, not a function.
  2. Step 2: Analyze Options

    GetIt.instance.registerSingleton(MyService()); correctly passes an instance: MyService(). GetIt.instance.registerSingleton(() => MyService()); wrongly passes a function. GetIt.instance.registerFactory(() => MyService()); registers a factory, not a singleton. GetIt.instance.registerLazySingleton(MyService()); passes an instance to lazy singleton incorrectly.
  3. Final Answer:

    GetIt.instance.registerSingleton(MyService()); -> Option B
  4. Quick Check:

    Singleton registration needs instance, not function [OK]
Hint: Singletons get instances, factories get functions [OK]
Common Mistakes:
  • Passing a function instead of an instance for singleton
  • Confusing registerFactory with registerSingleton
  • Using wrong syntax for lazy singleton
3. Given the code below, what will be printed?
final getIt = GetIt.instance;

class Counter {
  int value = 0;
  void increment() => value++;
}

void main() {
  getIt.registerSingleton(Counter());
  final counter1 = getIt<Counter>();
  final counter2 = getIt<Counter>();
  counter1.increment();
  print(counter2.value);
}
medium
A. 1
B. null
C. 0
D. Throws an error

Solution

  1. Step 1: Understand Singleton Behavior

    Registering Counter as a singleton means counter1 and counter2 point to the same instance.
  2. Step 2: Trace the Code Execution

    Calling counter1.increment() increases value from 0 to 1. Since counter2 is the same instance, counter2.value is 1.
  3. Final Answer:

    1 -> Option A
  4. Quick Check:

    Singleton shares state, so value = 1 [OK]
Hint: Singleton means one shared instance, so changes reflect everywhere [OK]
Common Mistakes:
  • Thinking counter2 is a new instance with value 0
  • Assuming increment does not affect shared instance
  • Expecting null or error without registration
4. What is wrong with this GetIt registration code?
final getIt = GetIt.instance;

void setup() {
  getIt.registerFactory<MyService>(MyService());
}
medium
A. registerFactory expects a function, not an instance
B. registerFactory cannot be called inside a function
C. MyService must be a singleton, not a factory
D. GetIt.instance should not be assigned to a variable

Solution

  1. Step 1: Check registerFactory Syntax

    The registerFactory method expects a function that returns a new instance each time, not an instance itself.
  2. Step 2: Identify the Error

    Passing MyService() directly is an instance, not a function. The correct usage is registerFactory(() => MyService()).
  3. Final Answer:

    registerFactory expects a function, not an instance -> Option A
  4. Quick Check:

    Factories need functions returning new instances [OK]
Hint: Factories need functions, singletons need instances [OK]
Common Mistakes:
  • Passing instance instead of function to registerFactory
  • Thinking registerFactory only works outside functions
  • Confusing singleton and factory registration
5. You want to register a service that should create a new instance every time it is requested. Which GetIt method should you use, and how should you register it?
hard
A. Use registerLazySingleton with a function returning a new instance
B. Use registerSingleton with an instance of the service
C. Use registerFactory with a function returning a new instance
D. Use registerSingleton with a function returning a new instance

Solution

  1. Step 1: Understand the Requirement

    The service must create a new instance every time it is requested, so it should not be a singleton.
  2. Step 2: Choose Correct GetIt Method

    registerFactory registers a function that returns a new instance on each call, matching the requirement.
  3. Step 3: Confirm Registration Syntax

    The correct syntax is registerFactory(() => Service()), passing a function that creates a new instance.
  4. Final Answer:

    Use registerFactory with a function returning a new instance -> Option C
  5. Quick Check:

    New instance each time = registerFactory with function [OK]
Hint: New instance each time? Use registerFactory with a function [OK]
Common Mistakes:
  • Using registerSingleton for new instances
  • Passing instance instead of function to registerFactory
  • Confusing lazy singleton with factory
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard