0Pricing
Flutter Mobile Development · Lección

Fundamentos del paquete Provider

Aprenda a utilizar el paquete Provider, una solución popular de Flutter, para gestionar el estado de forma sencilla y escalable en toda su aplicación.

Fundamentos del paquete Provider es una lección gratuita de Flutter Mobile Development en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Flutter Mobile Development, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Flutter Mobile Development incluye 4 lecciones en total.

Partes de esta lección aún no han sido traducidas y se muestran en inglés.

What is Provider?

The Provider package is a popular and simple solution for state management in Flutter. It's built on top of Flutter's own InheritedWidget, making it efficient and easy to understand.

It helps you share data and state across your widget tree without complex callbacks or prop drilling.

The Data Holder: ChangeNotifier

At the heart of Provider is the ChangeNotifier class. You extend this class for any object that holds state you want to share.

When your state changes, you call notifyListeners() to tell all listening widgets to rebuild.

import 'package:flutter/material.dart';

// 1. Extend ChangeNotifier
class MyCounter extends ChangeNotifier {
  int _count = 0; // Private state variable

  // Getter to access the count
  int get count => _count;

  // Method to modify state and notify listeners
  void increment() {
    _count++;
    notifyListeners(); // Important! Triggers UI updates
  }
}

Making State Available

To make your ChangeNotifier instance available to widgets, you use a ChangeNotifierProvider. You wrap a part of your widget tree (usually your whole app) with it.

The create callback provides an instance of your state object.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// Our state class from before
class MyCounter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    // Wrap the app with ChangeNotifierProvider
    ChangeNotifierProvider(
      create: (context) => MyCounter(), // Create an instance
      child: MyApp(), // The widget tree that can access MyCounter
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Provider Setup')),
        body: Center(
          child: Text(
            'MyCounter is now provided!',
            style: TextStyle(fontSize: 24),
          ),
        ),
      ),
    );
  }
}

Listening for UI Rebuilds

The most common way for a widget to listen to state changes and rebuild its UI is using context.watch(). This method makes the widget dependent on the provided state.

When notifyListeners() is called, any widget using watch will rebuild.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class MyCounter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => MyCounter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 1. Use context.watch<MyCounter>() to get the current state
    //    This widget will rebuild when MyCounter changes.
    final counter = context.watch<MyCounter>();

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Watch for Changes')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Count: ${counter.count}', // Display the count
                style: TextStyle(fontSize: 32),
              ),
              SizedBox(height: 20),
              // Button to increment (action will be added next)
              ElevatedButton(
                onPressed: () {
                  // How to trigger increment? See next scene!
                },
                child: Text('Increment (Not yet connected)'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Actions without Rebuilds

Sometimes you need to access state methods (like increment()) but don't want the widget to rebuild. For this, use context.read().

It provides a one-time access to the state object and does not establish a listening relationship.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class MyCounter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => MyCounter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<MyCounter>(); // Listen for UI updates

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Read for Actions')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Count: ${counter.count}',
                style: TextStyle(fontSize: 32),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  // 1. Use context.read<MyCounter>() to get the instance
                  // 2. Call the increment method
                  context.read<MyCounter>().increment();
                },
                child: Text('Increment Counter'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Alternative for One-Time Access

Before context.read(), the common way to access state without listening was Provider.of(context, listen: false).

It works exactly like context.read(), but read is often preferred for its conciseness. Both are suitable for event handlers like onPressed.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class MyCounter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => MyCounter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<MyCounter>(); // UI listens

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Provider.of(listen: false)')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Count: ${counter.count}',
                style: TextStyle(fontSize: 32),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  // Use Provider.of with listen: false for one-time access
                  Provider.of<MyCounter>(context, listen: false).increment();
                },
                child: Text('Increment Counter (using Provider.of)'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Fine-Grained Rebuilds with Consumer

The Consumer widget is another way to listen to state changes. Unlike context.watch(), which rebuilds the entire widget, Consumer only rebuilds its direct child (the builder function).

This can be useful for performance if only a small part of a large widget needs to react to state changes.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class MyCounter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => MyCounter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Consumer Widget')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'This text does NOT rebuild.',
                style: TextStyle(fontSize: 20, color: Colors.grey),
              ),
              SizedBox(height: 20),
              // Only the Text widget inside Consumer rebuilds
              Consumer<MyCounter>(
                builder: (context, counter, child) {
                  print('Consumer rebuilt!'); // See this in console
                  return Text(
                    'Count: ${counter.count}',
                    style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
                  );
                },
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            context.read<MyCounter>().increment();
          },
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

Providing Multiple States

For apps with multiple independent state objects, wrapping your app with many ChangeNotifierProviders can get messy. MultiProvider simplifies this.

It takes a list of providers, making your widget tree cleaner and easier to manage.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class MyCounter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

class MyThemeSettings extends ChangeNotifier {
  bool _isDark = false;
  bool get isDark => _isDark;
  void toggleTheme() {
    _isDark = !_isDark;
    notifyListeners();
  }
}

void main() {
  runApp(
    MultiProvider( // Use MultiProvider for multiple states
      providers: [
        ChangeNotifierProvider(create: (context) => MyCounter()),
        ChangeNotifierProvider(create: (context) => MyThemeSettings()),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Access both states using watch
    final counter = context.watch<MyCounter>();
    final theme = context.watch<MyThemeSettings>();

    return MaterialApp(
      theme: theme.isDark ? ThemeData.dark() : ThemeData.light(),
      home: Scaffold(
        appBar: AppBar(title: Text('MultiProvider Demo')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Count: ${counter.count}', style: TextStyle(fontSize: 28)),
              SizedBox(height: 20),
              Text('Dark Mode: ${theme.isDark ? "On" : "Off"}', style: TextStyle(fontSize: 28)),
              SizedBox(height: 30),
              ElevatedButton(
                onPressed: () => context.read<MyThemeSettings>().toggleTheme(),
                child: Text('Toggle Theme'),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => context.read<MyCounter>().increment(),
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

Provider Quick Check

Which of the following is the primary purpose of context.read() in the Provider package?

Provider Basics Recap

You've learned the essentials of the Provider package!

  • ChangeNotifier holds your application state.
  • ChangeNotifierProvider makes that state available.
  • context.watch() listens for state changes and rebuilds UI.
  • context.read() accesses state for actions without rebuilding.
  • Provider.of(listen: false) is an older alternative to read.
  • Consumer allows for fine-grained UI rebuilds.
  • MultiProvider helps manage multiple state objects cleanly.

Provider is a powerful and flexible tool for managing state in your Flutter apps!

Preguntas frecuentes

¿La lección «Fundamentos del paquete Provider» es gratis?

Sí — el texto completo de «Fundamentos del paquete Provider» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Flutter Mobile Development, actualiza a CoddyKit PRO. El curso de Flutter Mobile Development incluye 4 lecciones en total.

¿Qué aprenderé en «Fundamentos del paquete Provider»?

Aprenda a utilizar el paquete Provider, una solución popular de Flutter, para gestionar el estado de forma sencilla y escalable en toda su aplicación. Practicas Flutter Mobile Development con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Flutter Mobile Development?

No se requiere experiencia previa. Flutter Mobile Development en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Fundamentos del paquete Provider»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Flutter Mobile Development?

Sí. Cada lección de Flutter Mobile Development incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. setState e InheritedWidget
  2. Fundamentos del paquete Provider
  3. Riverpod para la gestión del estado
  4. Patrón BLoC con Streams
← Volver a Flutter Mobile Development