Created
October 15, 2025 08:52
-
-
Save 100xA/d93f72289928641b49a8e52a325efb85 to your computer and use it in GitHub Desktop.
Provider/ChangeNotifier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:provider/provider.dart'; | |
| void main() { | |
| runApp( | |
| MultiProvider( | |
| providers: [ | |
| ChangeNotifierProvider(create: (_) => CartModel()), | |
| Provider<ApiService>(create: (_) => ApiService()), | |
| ChangeNotifierProvider(create: (_) => CounterModel()), | |
| ], | |
| child: const MyApp(), | |
| ), | |
| ); | |
| } | |
| class CounterModel extends ChangeNotifier { | |
| int _count = 0; | |
| int get count => _count; | |
| void increment() { | |
| _count++; | |
| notifyListeners(); | |
| } | |
| } | |
| class CartModel extends ChangeNotifier { | |
| final Map<String, int> _lines = {}; | |
| Map<String, int> get lines => Map.from(_lines); | |
| void add(String productId) { | |
| _lines.update(productId, (q) => q + 1, ifAbsent: () => 1); | |
| notifyListeners(); | |
| } | |
| void clear() { | |
| _lines.clear(); | |
| notifyListeners(); | |
| } | |
| int get totalItems => _lines.values.fold(0, (a, b) => a + b); | |
| } | |
| class ApiService { | |
| List<Product> fetchProducts() { | |
| return const [ | |
| Product(id: 'a', name: 'Apfel', price: 9.99), | |
| Product(id: 'b', name: 'Banane', price: 12.50), | |
| Product(id: 'c', name: 'Brot', price: 7.25), | |
| ]; | |
| } | |
| } | |
| class Product { | |
| final String id; | |
| final String name; | |
| final double price; | |
| const Product({required this.id, required this.name, required this.price}); | |
| } | |
| class MyApp extends StatelessWidget { | |
| const MyApp({super.key}); | |
| @override | |
| Widget build(BuildContext context) { | |
| return MaterialApp( | |
| theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo), | |
| darkTheme: ThemeData.dark(useMaterial3: true), | |
| home: const DemoScreen(), | |
| ); | |
| } | |
| } | |
| class DemoScreen extends StatefulWidget { | |
| const DemoScreen({super.key}); | |
| @override | |
| State<DemoScreen> createState() => _DemoScreenState(); | |
| } | |
| class _DemoScreenState extends State<DemoScreen> { | |
| List<Product> _products = const []; | |
| int _localCount = 0; | |
| @override | |
| void initState() { | |
| super.initState(); | |
| final api = context.read<ApiService>(); | |
| _products = api.fetchProducts(); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| print('DemoScreen built'); | |
| final providerCount = context.watch<CounterModel>().count; | |
| return Scaffold( | |
| body: SafeArea( | |
| child: ListView( | |
| padding: const EdgeInsets.all(12), | |
| children: [ | |
| Card( | |
| child: ListTile( | |
| title: const Text('setState Counter'), | |
| subtitle: Text('count: $_localCount'), | |
| trailing: ElevatedButton.icon( | |
| icon: const Icon(Icons.add), | |
| label: const Text('Increment'), | |
| onPressed: () { | |
| setState(() { | |
| _localCount++; | |
| }); | |
| }, | |
| ), | |
| ), | |
| ), | |
| Card( | |
| child: ListTile( | |
| title: const Text('Provider Counter'), | |
| subtitle: Text('count: $providerCount'), | |
| trailing: ElevatedButton.icon( | |
| icon: const Icon(Icons.add), | |
| label: const Text('Increment'), | |
| onPressed: () => context.read<CounterModel>().increment(), | |
| ), | |
| ), | |
| ), | |
| const SizedBox(height: 8), | |
| const SizedBox(height: 8), | |
| Card( | |
| child: Column( | |
| children: [ | |
| const ListTile( | |
| title: Text('Products (Provider + Selector)'), | |
| subtitle: Text('Add to cart and watch rebuilds'), | |
| ), | |
| ..._products.map((p) => ProductTile(product: p)), | |
| const SizedBox(height: 8), | |
| Consumer<CartModel>( | |
| builder: (context, cart, _) => Padding( | |
| padding: const EdgeInsets.all(12.0), | |
| child: Row( | |
| children: [ | |
| Text('Items in cart: ${cart.totalItems}'), | |
| const Spacer(), | |
| OutlinedButton( | |
| onPressed: () => context.read<CartModel>().clear(), | |
| child: const Text('Clear cart'), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| class ProductTile extends StatelessWidget { | |
| const ProductTile({super.key, required this.product}); | |
| final Product product; | |
| @override | |
| Widget build(BuildContext context) { | |
| return Selector<CartModel, int>( | |
| selector: (context, cart) => cart.lines[product.id] ?? 0, | |
| builder: (context, qty, _) => ListTile( | |
| title: Text(product.name), | |
| subtitle: Text('€${product.price.toStringAsFixed(2)}'), | |
| trailing: Row( | |
| mainAxisSize: MainAxisSize.min, | |
| children: [ | |
| if (qty > 0) | |
| Container( | |
| padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), | |
| decoration: BoxDecoration( | |
| color: Colors.green.withValues(alpha: 0.15), | |
| borderRadius: BorderRadius.circular(12), | |
| ), | |
| child: Text('x$qty'), | |
| ), | |
| const SizedBox(width: 8), | |
| ElevatedButton( | |
| onPressed: () => context.read<CartModel>().add(product.id), | |
| child: const Text('Add'), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment