Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to invoke a rebuild of a stateless widget?

Tags:

flutter

Context

I have two stateless widgets (pages): HomePage and DetailsPage. Obviously the application starts and launches the HomePage. There is a button the user can press to navigate to the DetailsPage with a Navigator.pop() button to navigate back to the HomePage.

I know when the DetailsPage is done being used with the .whenComplete() method. It is at this point I want to rebuild the HomePage widget.

Code

This is the minimum reproduction of my behavior.

main.dart
import 'package:example/home.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: HomePage());
  }
}
home.dart
import 'package:example/details.dart';
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  static const name = 'Home Page';
  const HomePage() : super();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: MaterialButton(
          color: Colors.blue,
          textColor: Colors.white,
          child: Text(name),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: DetailsPage.builder),
            ).whenComplete(() => print('Rebuild now.'));
          },
        ),
      ),
    );
  }
}
details.dart
import 'package:flutter/material.dart';

class DetailsPage extends StatelessWidget {
  static const name = 'Details Page';
  static WidgetBuilder builder = (BuildContext _) => DetailsPage();
  const DetailsPage();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(name),
            MaterialButton(
              color: Colors.blue,
              textColor: Colors.white,
              child: Text('Go Back'),
              onPressed: () => Navigator.pop(context),
            ),
          ],
        ),
      ),
    );
  }
}

Question

How can I invoke a rebuild of this stateless widget (HomePage) at the .whenComplete() method callback?

like image 435
Apealed Avatar asked Jan 22 '21 14:01

Apealed


People also ask

How do you rebuild the stateless widget in Flutter?

Using setState to rebuild widgets Flutter gives you access to setState() . In this case, we have to ensure setState() has the new values. When setState() is called, Flutter will know to get these new values and mark the widget that needs to be rebuilt.

How do I force a widget to rebuild?

The solution is to pass a new Key to WidgetB every time we need it to be rebuilt: WidgetA will see that WidgetB has changed and will rebuild it when setState is called. In other words, whenever a stateful widget's Key property changes, calling setState on its parent will force a rebuild of the stateful widget.

How build () method works how it rebuild itself?

The build method is called any time you call setState , your widget's dependencies update, or any of the parent widgets are rebuilt (when setState is called inside of those). Your widget will depend on any InheritedWidget you use, e.g. Theme. of(context) , MediaQuery. of(context) etc.


1 Answers

You can force rebuild the widget tree as follows:

class RebuildController   {
  final GlobalKey rebuildKey = GlobalKey();
  
  void rebuild() {
    void rebuild(Element el) {
      el.markNeedsBuild();
      el.visitChildren(rebuild);
    }
    (rebuildKey.currentContext as Element).visitChildren(rebuild);
  }

}

class RebuildWrapper extends StatelessWidget  {
  
  final RebuildController controller;
  final Widget child;

  const RebuildWrapper({Key? key, required this.controller, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) => Container(
    key: controller.rebuildKey,
    child: child,
  );

}

In your case,

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  final RebuildController controller = RebuildController();

  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: RebuildWrapper(
        controller: controller,
        child: HomePage(
          rebuildController: controller,
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {

  static const name = 'Home Page';
  final RebuildController rebuildController;

  const HomePage({Key? key, required this.rebuildController}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('Hello there!');
    return Scaffold(
      body: Center(
        child: MaterialButton(
          color: Colors.blue,
          textColor: Colors.white,
          child: const Text(name),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: DetailsPage.builder),
            ).whenComplete(rebuildController.rebuild);
          },
        ),
      ),
    );
  }
  
}

class DetailsPage extends StatelessWidget {
  static const name = 'Details Page';
  static WidgetBuilder builder = (BuildContext _) => const DetailsPage();

  const DetailsPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(name),
            MaterialButton(
              color: Colors.blue,
              textColor: Colors.white,
              child: const Text('Go Back'),
              onPressed: () => Navigator.pop(context),
            ),
          ],
        ),
      ),
    );
  }
}

class RebuildController   {
  final GlobalKey rebuildKey = GlobalKey();
  
  void rebuild() {
    void rebuild(Element el) {
      el.markNeedsBuild();
      el.visitChildren(rebuild);
    }
    (rebuildKey.currentContext as Element).visitChildren(rebuild);
  }

}

class RebuildWrapper extends StatelessWidget  {
  
  final RebuildController controller;
  final Widget child;

  const RebuildWrapper({Key? key, required this.controller, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) => Container(
    key: controller.rebuildKey,
    child: child,
  );

}

But it is unnatural to force rebuild stateless widgets as they are not supposed to be rebuilt. You should use stateful widget or other state management solutions so that your HomePage will only be updated on meaningful state change.

Source - this answer

like image 58
saw Avatar answered Oct 29 '22 13:10

saw