Search code examples
flutterdartwebviewsetstatestatefulwidget

How to force a page rebuild with setStage() every time the dropdown button is changed? (Flutter)


I am trying to implement a dropdown button that let users switch between languages for my app, upon which the corresponding Webview containers will change to match the selected languages. I tried to implement this by creating a _renderWebviewBox1() function that returns the Webview controller that matches the current selected language.

String? _value = "English";

  _renderWebviewBox1() {
    if (_value == "English") {
      return WebViewWidget(controller: _controller);
    } else if (_value == "Mi'gmaq") {
      return WebViewWidget(controller: _controller1Mig);
    } else {
      print("No translation yet");
      return null;
    }
  }

Here is my full code:

import 'package:flutter/material.dart';
import 'package:flutter_webview_practice/routes.dart';

import 'package:webview_flutter/webview_flutter.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MainPage(),
      initialRoute: RouteManager.homePage,
      onGenerateRoute: RouteManager.generateRoute,
    );
  }
}

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

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  late final WebViewController _controller = WebViewController()
    ..setBackgroundColor(Colors.transparent)
    ..setJavaScriptMode(JavaScriptMode.unrestricted)
    ..loadFlutterAsset("assets/aligns/hunter-3-align1.html")
    ..setNavigationDelegate(
      NavigationDelegate(
        onProgress: (int progress) {
          debugPrint('WebView is loading (progress : $progress%)');
        },
      ),
    );

  late final WebViewController _controller2 = WebViewController()
    ..setBackgroundColor(Colors.transparent)
    ..setJavaScriptMode(JavaScriptMode.unrestricted)
    ..loadFlutterAsset("assets/aligns/hunter-3-align2.html")
    ..setNavigationDelegate(
      NavigationDelegate(
        onProgress: (int progress) {
          debugPrint('WebView is loading (progress : $progress%)');
        },
      ),
    );

  late final WebViewController _controller1Mig = WebViewController()
    ..setBackgroundColor(Colors.transparent)
    ..setJavaScriptMode(JavaScriptMode.unrestricted)
    ..loadFlutterAsset("assets/aligns/hunter-3-align1-mig.html")
    ..setNavigationDelegate(
      NavigationDelegate(
        onProgress: (int progress) {
          debugPrint('WebView is loading (progress : $progress%)');
        },
      ),
    );

  late final WebViewController _controller2Mig = WebViewController()
    ..setBackgroundColor(Colors.transparent)
    ..setJavaScriptMode(JavaScriptMode.unrestricted)
    ..loadFlutterAsset("assets/aligns/hunter-3-align2-mig.html")
    ..setNavigationDelegate(
      NavigationDelegate(
        onProgress: (int progress) {
          debugPrint('WebView is loading (progress : $progress%)');
        },
      ),
    );

  String? _value = "English";

  _renderWebviewBox1() {
    if (_value == "English") {
      return WebViewWidget(controller: _controller);
    } else if (_value == "Mi'gmaq") {
      return WebViewWidget(controller: _controller1Mig);
    } else {
      print("No translation yet");
      return null;
    }
  }

  _renderWebviewBox2() {
    if (_value == "English") {
      return WebViewWidget(controller: _controller2);
    } else if (_value == "Mi'gmaq") {
      return WebViewWidget(controller: _controller2Mig);
    } else {
      print("No translation yet");
      return null;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FittedBox(
          alignment: Alignment.center,
          fit: BoxFit.contain,
          child: Column(
            children: [
              DropdownButton<String>(
                value: _value,
                items: <String>[
                  "English",
                  "Mi'gmaq",
                  "Wolastoqey",
                ].map((String value) {
                  return DropdownMenuItem<String>(
                    value: value,
                    child: Text(value),
                  );
                }).toList(),
                onChanged: (String? newValue) {
                  if (newValue != _value) {
                    switch (newValue) {
                      case "Mi'gmaq":
                        _value = "Mi'gmaq";
                        break;
                      case "English":
                        _value = "English";
                        break;
                      case "Wolastoqey":
                        _value = "Wolastoqey";
                        break;
                    }
                  }
                  setState(() {
                    _value = newValue;
                  });
                },
              ),
              Stack(
                children: [
                  Container(
                    width: 650,
                    height: 500,
                    child: const Image(
                      image: AssetImage("assets/images/hunterChoice3.jpg"),
                    ),
                  ),
                  Positioned(
                    left: 120,
                    right: 10,
                    top: 10,
                    bottom: 10,
                    child: Container(
                      width: 325,
                      height: 500,
                      child: _renderWebviewBox1(),
                    ),
                  ),
                  Positioned(
                    left: 20,
                    right: 90,
                    top: 420,
                    child: Container(
                      width: 325,
                      height: 500,
                      child: _renderWebviewBox2(),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

However, currently, while the dropdown button does update, the page does not rebuild and thus _renderWebviewBox1() and _renderWebviewBox2() are only called once. How should I implement setState() such that the page rebuilds every time the dropdown button is changed?

I have tried placing setState() inside each of the switch statement of the onChanged property in the dropdown button widget, but that also does not rebuild the page.


Solution

  • As Peter Koltai mentioned, you should use the setState method to let the application know that it must rebuild, but it is not necessary to use the _value in the build method directly. When the build method is executed, it also executes both functions every time.

    I assume that if you add a different key to each of your WebViewWidget, it would rebuild normally.

    Also, you don't need that switch statement completely, it can be removed as it does nothing:

    onChanged: (String? newValue) {
      if (newValue != _value) {
        setState(() {
          _value = newValue;
        });
    }
    

    Moreover, as you use copy-paste code everywhere, consider creating functions or methods to reduce code duplication.