Why won’t Provider rerun its create() when its parent StatelessWidget is re-running build()?

I know the more common ways to handle this kind of a thing involve using a ChangeNotifierProvider(CNP), etc., but I want to understand how Provider actually works.

If I have a StatelessWidget with a Provider somewhere in its widget tree descendents, and that StatelessWidget gets recreated (i.e., its build() method runs again), I would expect a new Provider to be created. So, if the Provider is being created from a value that has been passed to that StatelessWidget, I would expect a new Provider to be created with that new value. Then, any downstream widgets that are using Provider.of<T>(context, listen:true) would update…I would think.

…But that doesn’t happen. When that parent StatelessWidget is rebuilt with a new input value, the Provider doesn’t rebuild itself. Why not?

Again, I’m not saying this would be a great approach, I just want to understand why this is happening like this.

Here is some basic code to show what I mean:
Here it is in DartPad.

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

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: CounterUpper(),
        ),
      ),
    );
  }
}

class _CounterUpperState extends State<CounterUpper>
    with TickerProviderStateMixin {
  Ticker? ticker;
  double animationValue = 0;

  @override
  void initState() {
    print('Running _CenterUpperState.initState()');
    super.initState();
    ticker = Ticker((Duration elapsed) {
      if (elapsed.inSeconds - animationValue > 1) {
        setState(() {
          print(
              'Running _CenterUpperState.setState() with animationValue: $animationValue and elapsed.inSeconds: ${elapsed.inSeconds}');
          animationValue = elapsed.inSeconds.toDouble();
        });
      }
      if (elapsed.inSeconds > 5) {
        ticker?.stop();
      }
    });
    ticker!.start();
  }

  @override
  Widget build(BuildContext context) {
    print(
        'Running _CenterUpperState.build() with animationValue: $animationValue');
    return ProviderHolder(animationValue: animationValue);
  }
}

class ProviderHolder extends StatelessWidget {
  const ProviderHolder({super.key, required this.animationValue});

  final double animationValue;

  @override
  Widget build(BuildContext context) {
    print(
        'Running ProviderHolder.build() with animationValue: $animationValue');
    return Provider<ValueWrapper>(
      create: (_) {
        print('Running Provider.create() with animationValue: $animationValue');
        return ValueWrapper(animationValue);
      },
      child: ContentHolder(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final double providerValue =
        Provider.of<ValueWrapper>(context, listen: true).value;
    print('Running ContentHolder.build() with providerValue: $providerValue');
    return Text(providerValue.toString());
  }
}

class ValueWrapper {
  const ValueWrapper(this.value);

  final double value;
}

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

  @override
  _CounterUpperState createState() => _CounterUpperState();
}

I’ve tried re-arranging this many different ways (different versions of the parent widget including StatefulWidget). I understand that the most common way to do this would be to use a Provider of some object that can itself be manipulated. Something like a basic class that contains an integer as well as some methods to alter the value of that integer. Then, instead of re-creating the parent widget, you would just use those methods to change the value.

But still…why doesn’t this work? It is acting like the Provider is defined as const even though it has been passed a value that is not const.

Provider doesn’t call create again on rebuild, it has it’s own handling to look at the build context that you give it to find a state object and re-use it. as you had already deduced from your own logging, the create log only gets called once, this is how the create function is handled inside of provider (Even though rebuild runs, provider chooses to ignore your create script as to not recreate objects that already exist, this prevents expensive recreation on a value that already exists).

As with programming that leaves a few options,

  1. Handle the rebuild of the ProviderHolder to dispose entirely of the Provider everytime that it rebuilds, this will cause children to rebuild and the children to grab a new copy from the value provider. This is inneficient because in memory you are destroying the whole object and recreating it. Plus any initialization it may invoke in larger classes.
  2. Don’t use provider, if you’re doing something small like updating an animation value down 2 levels of your app, just pass the values through manually, it might look messy having a value passed directly but it will be more maintainable than an entire state object. Especially for localized animation items, unless you are doing something quite complex.
  3. As you already knew about, move your logic into your provider so that you can have 1 state object that gets created once, updates its own paramaters and then sends the build instruction to the widgets that needs it based on who is listening to updates with a change notifier.

Hope this makes sense, as with any package, you have to understand although that some choices may not make sense to you as you’d rather it destroy it’s previous state and rebuild from scratch, optimizations may be made to instead assist the general public who already have a provider and want it’s own state to remain unchanged. Hence why worded as oncreate to avoid confusion that it is getting rebuilt on every local state change.

Options 3 here is what I would do, something like this (done quickly so could be optimized, however it works as a rudimentary example):

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

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: CounterUpper(),
        ),
      ),
    );
  }
}

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

  @override
  State<CounterUpper> createState() => _CounterUpperState();
}

class _CounterUpperState extends State<CounterUpper> with TickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<ValueWrapper>(create: (_) => ValueWrapper(), child: const ContentHolder());
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Text(Provider.of<ValueWrapper>(context, listen: true).animationValue.toString());
  }
}

class ValueWrapper with ChangeNotifier {
  ValueWrapper() : animationValue = 0 {
    ticker = Ticker((Duration elapsed) {
      if (elapsed.inSeconds - animationValue > 1) {
        updateAnimationValue(elapsed.inSeconds.toDouble());
        animationValue = elapsed.inSeconds.toDouble();
      }
      if (elapsed.inSeconds > 5) {
        ticker.stop();
      }
    });
    ticker.start();
  }

  double animationValue;
  late Ticker ticker;

  void updateAnimationValue(double value) {
    animationValue = value;
    notifyListeners();
  }
}

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật