Bird
Raised Fist0
Fluttermobile~20 mins

InheritedWidget concept in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Theme Color Changer
A simple app screen that uses InheritedWidget to share a theme color across widgets. The user can tap a button to change the color, and all widgets using the shared color update automatically.
Target UI
┌─────────────────────────────┐
│       Theme Color Changer    │
├─────────────────────────────┤
│                             │
│   Current Color: [ColorBox] │
│                             │
│   [Change Color Button]      │
│                             │
│   Text using theme color     │
│                             │
└─────────────────────────────┘
Create a custom InheritedWidget to hold a Color value.
Provide a way to update the color from a button tap.
Widgets that read the color should rebuild automatically when it changes.
Display a colored box showing the current color.
Display a button labeled 'Change Color' that cycles through a list of colors.
Starter Code
Flutter
import 'package:flutter/material.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Color _color = Colors.blue;

  void _changeColor() {
    // TODO: Implement color change logic
  }

  @override
  Widget build(BuildContext context) {
    return ColorProvider(
      color: _color,
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: const Text('Theme Color Changer')),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                // TODO: Add ColorBox widget here
                const SizedBox(height: 20),
                ElevatedButton(
                  onPressed: _changeColor,
                  child: const Text('Change Color'),
                ),
                const SizedBox(height: 20),
                // TODO: Add Text widget that uses the color from ColorProvider
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class ColorProvider extends InheritedWidget {
  final Color color;

  const ColorProvider({required this.color, required super.child, super.key});

  static ColorProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ColorProvider>();
  }

  @override
  bool updateShouldNotify(ColorProvider oldWidget) {
    return oldWidget.color != color;
  }
}
Task 1
Task 2
Task 3
Solution
Flutter
import 'package:flutter/material.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final List<Color> _colors = [Colors.blue, Colors.red, Colors.green, Colors.orange];
  int _currentIndex = 0;

  void _changeColor() {
    setState(() {
      _currentIndex = (_currentIndex + 1) % _colors.length;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ColorProvider(
      color: _colors[_currentIndex],
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: const Text('Theme Color Changer')),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                ColorBox(),
                const SizedBox(height: 20),
                ElevatedButton(
                  onPressed: _changeColor,
                  child: const Text('Change Color'),
                ),
                const SizedBox(height: 20),
                ThemedText(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class ColorProvider extends InheritedWidget {
  final Color color;

  const ColorProvider({required this.color, required super.child, super.key});

  static ColorProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ColorProvider>();
  }

  @override
  bool updateShouldNotify(ColorProvider oldWidget) {
    return oldWidget.color != color;
  }
}

class ColorBox extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final color = ColorProvider.of(context)?.color ?? Colors.black;
    return Container(
      width: 100,
      height: 100,
      decoration: BoxDecoration(
        color: color,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: Colors.black26),
      ),
    );
  }
}

class ThemedText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final color = ColorProvider.of(context)?.color ?? Colors.black;
    return Text(
      'This text uses the theme color',
      style: TextStyle(fontSize: 18, color: color),
    );
  }
}

We created a ColorProvider class that extends InheritedWidget. It holds a Color value and notifies dependents when the color changes.

The MyApp stateful widget manages the current color index and cycles through a list of colors when the button is pressed. It rebuilds the ColorProvider with the new color.

The ColorBox and ThemedText widgets read the current color from ColorProvider.of(context). Because they depend on the inherited widget, they rebuild automatically when the color changes.

This shows how InheritedWidget shares data down the widget tree and triggers rebuilds efficiently.

Final Result
Completed Screen
┌─────────────────────────────┐
│       Theme Color Changer    │
├─────────────────────────────┤
│                             │
│   Current Color: [████████] │
│                             │
│   [Change Color Button]      │
│                             │
│   This text uses the theme   │
│   color                     │
│                             │
└─────────────────────────────┘
User taps 'Change Color' button.
The colored box changes to the next color in the list.
The text color updates to match the new theme color.
Stretch Goal
Add a reset button that sets the color back to the first color in the list.
💡 Hint
Add another ElevatedButton labeled 'Reset' that calls setState to set _currentIndex to 0.

Practice

(1/5)
1. What is the main purpose of an InheritedWidget in Flutter?
easy
A. To handle user input events.
B. To create animations between widgets.
C. To manage network requests.
D. To share data efficiently down the widget tree without passing it manually.

Solution

  1. Step 1: Understand the role of InheritedWidget

    InheritedWidget is designed to share data with its child widgets efficiently.
  2. Step 2: Compare with other options

    Animations, user input, and network requests are handled by other Flutter classes, not InheritedWidget.
  3. Final Answer:

    To share data efficiently down the widget tree without passing it manually. -> Option D
  4. Quick Check:

    InheritedWidget shares data down the tree = C [OK]
Hint: InheritedWidget shares data down widget tree [OK]
Common Mistakes:
  • Confusing InheritedWidget with animation or input handling
  • Thinking it manages network calls
  • Assuming it rebuilds widgets manually
2. Which of the following is the correct way to access data from an InheritedWidget named MyData inside a child widget?
easy
A. MyData.get(context)
B. MyData.of(context)
C. MyData.fetch(context)
D. MyData.access(context)

Solution

  1. Step 1: Recall the standard access method

    The convention to access InheritedWidget data is using a static of method.
  2. Step 2: Verify method names

    Only MyData.of(context) follows Flutter's standard pattern; others are incorrect.
  3. Final Answer:

    MyData.of(context) -> Option B
  4. Quick Check:

    Use of() method to access InheritedWidget data = A [OK]
Hint: Use .of(context) to get InheritedWidget data [OK]
Common Mistakes:
  • Using non-existent methods like get or fetch
  • Trying to access data without context
  • Confusing with Provider package syntax
3. Given this code snippet, what will be printed when MyInheritedWidget updates its data and updateShouldNotify returns true?
class MyInheritedWidget extends InheritedWidget {
  final int data;
  MyInheritedWidget({required this.data, required Widget child}) : super(child: child);

  static MyInheritedWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  }

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return data != oldWidget.data;
  }
}

// In a child widget's build:
final inherited = MyInheritedWidget.of(context);
print('Data is: ${inherited?.data}');
medium
A. No output printed
B. Data is: old data value
C. Data is: new data value
D. Runtime error occurs

Solution

  1. Step 1: Understand updateShouldNotify behavior

    When updateShouldNotify returns true, dependent widgets rebuild and get new data.
  2. Step 2: Analyze the print statement

    The child widget accesses the updated data value, so it prints the new data.
  3. Final Answer:

    Data is: new data value -> Option C
  4. Quick Check:

    updateShouldNotify true triggers rebuild with new data = A [OK]
Hint: True updateShouldNotify means child rebuilds with new data [OK]
Common Mistakes:
  • Assuming old data is printed after update
  • Thinking no rebuild happens
  • Expecting runtime errors from updateShouldNotify
4. Identify the error in this InheritedWidget implementation:
class MyData extends InheritedWidget {
  final int value;
  MyData({required this.value, required Widget child}) : super(child: child);

  static MyData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyData>()!;
  }

  @override
  bool updateShouldNotify(MyData oldWidget) {
    return false;
  }
}
medium
A. updateShouldNotify always returns false, so dependents never rebuild.
B. Missing call to super constructor.
C. Static method of() should return nullable type.
D. value field should be mutable.

Solution

  1. Step 1: Check updateShouldNotify logic

    Returning false always means widgets depending on this InheritedWidget won't rebuild on data change.
  2. Step 2: Verify other parts

    Constructor calls super correctly; of() returns non-null with !; value can be final.
  3. Final Answer:

    updateShouldNotify always returns false, so dependents never rebuild. -> Option A
  4. Quick Check:

    False updateShouldNotify blocks rebuilds = D [OK]
Hint: updateShouldNotify false means no rebuilds triggered [OK]
Common Mistakes:
  • Ignoring updateShouldNotify effect on rebuild
  • Thinking constructor call is missing
  • Confusing nullable return types
  • Assuming value must be mutable
5. You want to create a theme color shared across your app using InheritedWidget. Which approach correctly ensures that widgets rebuild only when the color changes?
class ThemeColor extends InheritedWidget {
  final Color color;
  ThemeColor({required this.color, required Widget child}) : super(child: child);

  static ThemeColor? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ThemeColor>();
  }

  @override
  bool updateShouldNotify(ThemeColor oldWidget) {
    // What should go here?
  }
}
hard
A. return color != oldWidget.color;
B. return true;
C. return false;
D. return oldWidget.color == null;

Solution

  1. Step 1: Understand updateShouldNotify purpose

    This method decides if dependents rebuild when data changes.
  2. Step 2: Compare current and old color values

    Return true only if the color changed to trigger rebuilds correctly.
  3. Step 3: Evaluate options

    return color != oldWidget.color; returns true if colors differ, which is correct. return true; always rebuilds unnecessarily, return false; never rebuilds, return oldWidget.color == null; is irrelevant.
  4. Final Answer:

    return color != oldWidget.color; -> Option A
  5. Quick Check:

    Rebuild only if color changes = B [OK]
Hint: Compare old and new data in updateShouldNotify [OK]
Common Mistakes:
  • Always returning true causing unnecessary rebuilds
  • Always returning false blocking updates
  • Checking for null instead of value change
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