Progress Bar, Stack

LinearProgressIndicator and CircularProgressIndicator split on one parameter (value: null spins forever, a 0-to-1 double measures), and Stack composes them into the loading overlay every booking flow needs.

10 min read · 9 cards · 2 checks

Read in: English · हिन्दी · ગુજરાતી


Theory

Two seconds of doubt

Tap Book, and FestConnect Flutter spends 2 seconds saving to the server. Two seconds in which an unresponsive screen makes every student tap again: double bookings, support queue, chaos.

Unit 1 solved this on Android with a ProgressBar and visibility juggling. Flutter's version is leaner: 2 indicator widgets split by a single parameter, and: because everything is composition here: the professional loading overlay, built from a widget you already know: Stack, this topic's second name.

Theory

The 2 indicators, 1 deciding parameter

  • LinearProgressIndicator: a horizontal bar
  • CircularProgressIndicator: the spinner

Both obey one switch, the value: parameter:

  • value: null (or simply omitted): indeterminate: animates forever, promising only "working": right for unknowable durations (network saves)
  • value: 0.0 to 1.0: determinate: displays that fraction: 3 of 5 pass photos uploaded is value: 0.6

Note the scale: 0 to 1 as a double, not Android's setMax(100)/setProgress(60): the Unit 1 twin with different units, and a favourite compare question.

Practical

The loading overlay: Stack + indicator + a bool

class _BookingState extends State<BookingScreen> {
  bool saving = false;

  void book() {
    setState(() { saving = true; });        // show the overlay
    saveToServer().then((_) {
      setState(() { saving = false; });     // hide in the completion path
    });
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        bookingForm(),                       // the page itself

        if (saving) ...[                     // conditional children
          Positioned.fill(                   // translucent blanket
            child: Container(color: Colors.black54),
          ),
          const Center(                      // the spinner on top
            child: CircularProgressIndicator(),   // value: null = spins
          ),
        ],
      ],
    );
  }
}
// Determinate cousin, for the 5-photo upload:
// LinearProgressIndicator(value: uploaded / 5)   // 3 of 5 -> 0.6

Theory

Stack, the deeper pass

The invisible-widgets lesson introduced Stack as FrameLayout's twin; the overlay uses its 2 power tools:

  • Positioned.fill: stretches a child over the ENTIRE stack: the translucent Container blankets the whole form, absorbing stray taps
  • non-positioned children obey the stack's alignment (or a Center wrapper, as here): the spinner floats mid-screen

And the conditional if (saving) ...[ ] inside the children list is Flutter's idiom for widgets that exist only sometimes: the bool decides, setState flips it, the rebuild adds or removes the overlay: state deciding STRUCTURE, not just values.

Quiz

What is the difference between CircularProgressIndicator() and CircularProgressIndicator(value: 0.6)?

  1. None: value only changes the size
  2. The first spins forever (indeterminate); the second shows a fixed 60% arc (determinate)
  3. The first is invisible until value is supplied
  4. value: 0.6 makes it spin at 60% speed
Show the answer

The first spins forever (indeterminate); the second shows a fixed 60% arc (determinate)

One parameter carries the whole semantic split: null/omitted means "working, duration unknown": the eternal animation: while a 0-to-1 double means "this exact fraction done": a still arc at 60%. Choosing between them is honesty about what you know: a network save is indeterminate, a 5-photo upload is measurably determinate (uploaded / 5). Options C and D invent behaviours: the indicator is always visible once built, and value never modulates speed. And mind the scale trap for Android veterans: 0.6, never 60.

Think first

Audit the overlay's life cycle

Walk the listing's book() flow: what appears when, what does the black54 Container actually contribute besides dimming, and: recalling Unit 1's Android ProgressBar rule: what failure path is this simplified code not yet handling?

Show the answer

Tap: setState(saving = true): rebuild adds the blanket + spinner: the save runs behind them: .then setStates false: rebuild removes both. The Container's second job is armour: Positioned.fill means it INTERCEPTS every tap, so the double-booking double-tap dies at the blanket. The missing path: failure: if saveToServer errors, .then never runs and the overlay spins forever: Unit 1's rule (hide in EVERY outcome path) demands a .catchError that also setStates saving = false: the eternal-spinner bug, Flutter edition.

Watch out

Indicator and overlay slips

value: 60: the 0-to-1 scale means 60 is clamped nonsense; fractions, not percentages.

Overlay without the blanket: a naked spinner over a live form still lets taps through: Positioned.fill Container is the tap shield.

Hiding only on success: the reveal's eternal spinner: every path (success, error) must lower the flag.

Indeterminate for measurable work: a 5-photo upload with a mystery spinner wastes information the user would love to have.

Theory

Composition is the Flutter answer

Notice what Flutter did NOT give you: a LoadingOverlay widget. It gave you Stack, Positioned.fill, a Container, an indicator and a bool: and the overlay is their COMPOSITION, 10 honest lines. That is the framework's philosophy in one exhibit: few primitives, infinite assemblies: the same lesson as Unit 1's hand-composed ImageSlider. Next: the fest's event list returns one final time: Flutter's Lists, where ListView.builder completes the adapter story begun in this subject's very first lesson.

Summary

Key takeaways

  • LinearProgressIndicator = bar, CircularProgressIndicator = spinner; one widget family, 2 shapes.
  • value: null/omitted = indeterminate (spins forever); value: 0.0-1.0 = determinate fraction: 0-to-1, never 0-to-100.
  • Indeterminate for unknowable waits (network), determinate for measurable ones (n of 5 uploads).
  • The loading overlay = Stack + Positioned.fill translucent Container (tap shield) + centered spinner + a saving bool.
  • if (cond) ...[ ] in a children list: state deciding structure.
  • Hide the overlay in EVERY completion path or it spins forever.
  • Memory hook: null spins, fractions fill, the blanket eats stray taps.

Study this properly

This page is the lesson to read. In Gri-Learn the same topic is a graded deck: the self-checks are scored and your weak topics are tracked. Free to start.

Start this topic

Already have an account? Sign in

More from Flutter widget (Constructor, attributes and Properties)

Gri-Learn · syllabus-mapped B.C.A. lessons in English, Hindi and Gujarati

Progress Bar, Stack · Mobile Application Development - 2 (option B) · Gri-Learn