Как оптимизировать код (dart, flutter)?

Есть приложение где происходит роутинг между страницами. Сами странички очень типичные. Интересует как можно оптимизировать код чтобы не повторять его какждой странице. Там отличаются всего неcколько значений. У всех переменных на каждой странице название в зависимости от ее номера: buttonText1, _MyCustomFormState1 и тд.

Я пометил !!!ОТЛИЧАЕТСЯ!!! то что кардинально отличается. Я бы это все запихнул в функцию, где на входе отличающиеся элементы, но не понятно как это делать с виджетами...

Буду благодарен за любую наводку.

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:firebase_admob/firebase_admob.dart';
import 'package:math/settings.dart';
import 'package:http/http.dart' as http;

String text1;
int _coins1 = 0;
final UnitId = 'ca-app-pub-3940256099942544/1033173712';
const String testDevice = "8C804F944FA76E19B25112CFF43BED32";
String buttonText1 = "Подсказка";
Color buttonColor1 = Colors.blue;


class FirstRoute extends StatefulWidget {
  @override
  _MyCustomFormState1 createState() => _MyCustomFormState1();
}


class _MyCustomFormState1 extends State<FirstRoute> {
  static const MobileAdTargetingInfo targetingInfo = MobileAdTargetingInfo(
      testDevices: testDevice != null ? <String>[testDevice] : null,
      nonPersonalizedAds: true,
      keywords: <String>['Game']);

  Future<bool> internet() async {
    try {
      final result = await InternetAddress.lookup('google.com');
      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
        return true;
      }
    } on SocketException catch (_) {
      return false;
    }
  }

  @override
  void initState() {

    getCredential();

    FirebaseAdMob.instance
        .initialize(appId: 'ca-app-pub-9594082506249372~8751878673');

    RewardedVideoAd.instance.listener =
        (RewardedVideoAdEvent event, {String rewardType, int rewardAmount}) {

      if (event == RewardedVideoAdEvent.loaded) {
        RewardedVideoAd.instance.show();
        setState(() {
          MyOverlay.hide();
        });
      }

      if (event == RewardedVideoAdEvent.failedToLoad) {
        setState(() {
          MyOverlay.hide();
        });
      }

      if (event == RewardedVideoAdEvent.rewarded) {
        setState(() {
          if (_coins1 >= 10) {
            buttonText1 = "Решение";
            buttonColor1 = Colors.green;
            _coins1 += rewardAmount;
            showDialog(
              //barrierDismissible: false,
              context: context,
              builder: (BuildContext context) => _Reshenie(context),
            );
          }

          if (_coins1 == 0) {
            buttonText1 = "Решение";
            buttonColor1 = Colors.green;
            _coins1 += rewardAmount;
            showDialog(
              context: context,
              builder: (BuildContext context) => _Podskazka(context),
            );
          }
        });
      }
    };
    super.initState();
  }

  final myController1 = TextEditingController();
  SharedPreferences sharedPreferences1;


  @override
  void dispose() {
    myController1.dispose();
    super.dispose();
  }

  _onChanged(String text) async {
    sharedPreferences1 = await SharedPreferences.getInstance();
    sharedPreferences1.setString("level", text);
    getCredential();
  }

  getCredential() async {
    sharedPreferences1 = await SharedPreferences.getInstance();
    setState(() {
      text1 = sharedPreferences1.getString("level");
    });
  }

  Widget _Reshenie(BuildContext context) {
    return new AlertDialog(
      title: const Text('Решение'),
      shape:
          RoundedRectangleBorder(borderRadius: new BorderRadius.circular(15)),
      content: new Image.asset(
        "assets/images/1_1.jpg", !!!ОТЛИЧАЕТСЯ!!!
      ),
      actions: <Widget>[
        FlatButton.icon(
          shape: new RoundedRectangleBorder(
              borderRadius: new BorderRadius.circular(5.0),
              side: BorderSide(color: Colors.transparent)),
          color: Theme.of(context).accentColor,
          splashColor: Colors.amberAccent,
          textColor: const Color(0xFFFFFFFF),
          icon: Icon(Icons.close),
          label: Text('Все понятно'),
          onPressed: () {
            Navigator.of(context).pop();
          },
        )
      ],
    );
  }

  Widget _Podskazka(BuildContext context) {
    return new AlertDialog(
      title: const Text('Подсказка'),
      shape:
          RoundedRectangleBorder(borderRadius: new BorderRadius.circular(15)),
      content: Text('+',
          textAlign: TextAlign.center, style: TextStyle(fontSize: 65)),
      actions: <Widget>[
        FlatButton.icon(
          shape: new RoundedRectangleBorder(
              borderRadius: new BorderRadius.circular(5.0),
              side: BorderSide(color: Colors.transparent)),
          color: Theme.of(context).accentColor,
          splashColor: Colors.amberAccent,
          textColor: const Color(0xFFFFFFFF),
          icon: Icon(Icons.close),
          //`Icon` to display
          label: Text('Все понятно'),
          //`Text` to display
          onPressed: () {
            Navigator.of(context).pop();
          },
        )
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
        onWillPop: () {
          Navigator.pushNamedAndRemoveUntil(
              context, "/", (Route<dynamic> route) => false);
          return Future.value(false);
        },
        child: Scaffold(
          appBar: AppBar(
            title: Text('MATH'),
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () => Navigator.pushNamedAndRemoveUntil(
                  context, "/", (Route<dynamic> route) => false),
            ),
          ),
          body: Container(
            child: Center(
                child: SingleChildScrollView(
              child: Column(
                children: <Widget>[
                  new Image.asset('assets/images/1.jpg'),!!!ОТЛИЧАЕТСЯ!!!
                  new TextField(
                      controller: myController1,
                      keyboardType: TextInputType.number,
                      autofocus: false,
                      style: TextStyle(fontSize: 24.0, color: Colors.black),
                      decoration: InputDecoration(
                          filled: true,
                          fillColor: Colors.white,
                          hintText: 'Введите ответ',
                          contentPadding: const EdgeInsets.only(
                              left: 14.0, bottom: 8.0, top: 8.0),
                          focusedBorder: OutlineInputBorder(
                            borderSide: BorderSide(color: Colors.white),
                            borderRadius: BorderRadius.circular(25.7),
                          ),
                          enabledBorder: UnderlineInputBorder(
                            borderSide: BorderSide(color: Colors.white),
                            borderRadius: BorderRadius.circular(25.7),
                          ))),
                  FlatButton.icon(
                    shape: new RoundedRectangleBorder(
                        borderRadius: new BorderRadius.circular(5.0),
                        side: BorderSide(color: Colors.transparent)),
                    color: buttonColor1,
                    splashColor: Colors.amberAccent,
                    textColor: const Color(0xFFFFFFFF),
                    icon: Icon(Icons.help),
                    label: Text(buttonText1,
                        style: TextStyle(
                            fontSize: 18, decoration: TextDecoration.none)),
                    onPressed: () {

                      FocusScope.of(context).requestFocus(FocusNode());

                      internet().then((result) {
                        if (result == false) {
                          MyOverlay.hide();
                          setState(() {
                            if (_coins1 >= 10) {
                              buttonText1 = "Решение";
                              buttonColor1 = Colors.green;
                              _coins1 += 10;
                              showDialog(
                                //barrierDismissible: false,
                                context: context,
                                builder: (BuildContext context) =>
                                    _Reshenie(context),
                              );
                            }

                            if (_coins1 == 0) {
                              buttonText1 = "Решение";
                              buttonColor1 = Colors.green;
                              _coins1 += 10;
                              showDialog(
                                context: context,
                                builder: (BuildContext context) =>
                                    _Podskazka(context),
                              );
                            }
                          });
                        } else {
                          if (_coins1 < 10) {
                            MyOverlay.show(context);
                            RewardedVideoAd.instance.load(
                                adUnitId: RewardedVideoAd.testAdUnitId,
                                targetingInfo: targetingInfo);
                          }

                          if (_coins1 == 10) {
                            MyOverlay.show(context);
                            RewardedVideoAd.instance.load(
                                adUnitId: RewardedVideoAd.testAdUnitId,
                                targetingInfo: targetingInfo);
                            setState(() {
                              buttonColor1 = Colors.green;
                              buttonText1 = "Решение";
                            });
                          }

                          if (_coins1 > 10) {
                            setState(() {
                              buttonColor1 = Colors.green;
                              buttonText1 = "Решение";
                            });
                            showDialog(
                                //barrierDismissible: false,
                                context: context,
                                builder: (BuildContext context) =>
                                    _Reshenie(context));
                          }
                        }
                      });
                    },
                  ),
                ],
              ),
            )),
          ),
          floatingActionButton: FloatingActionButton.extended(
              label: Text('Ответ',
                  style:
                      TextStyle(fontSize: 16, decoration: TextDecoration.none)),

              onPressed: () {
                if (myController1.text == "7") { !!!ОТЛИЧАЕТСЯ!!!
                  showDialog(
                    context: context,
                    barrierDismissible: false,
                    builder: (context) {
                      return AlertDialog(
                        content: Container(
                          height: 75.0,
                          width: 60.0,
                          child: Column(children: <Widget>[
                            Icon(Icons.check_circle,
                                color: Colors.greenAccent.shade400, size: 50.0),
                            Text("Правильно")
                          ]),
                        ),
                      );
                    },
                  );
                  _onChanged("2");!!!ОТЛИЧАЕТСЯ!!!
                  setState(() {
                    _coins1 = 0;
                  });
                  new Timer(const Duration(seconds: 2),
                      () => Navigator.pushNamed(context, "/" + text1));
                } else {
                  showDialog(
                      context: context,
                      builder: (context) {
                        return AlertDialog(
                          content: Container(
                            height: 75.0,
                            width: 60.0,
                            child: Column(children: <Widget>[
                              Icon(Icons.cancel,
                                  color: Colors.deepOrange.shade400,
                                  size: 50.0),
                              Text("Неправильно")
                            ]),
                          ),
                          actions: <Widget>[
                            FlatButton.icon(
                              shape: new RoundedRectangleBorder(
                                  borderRadius: new BorderRadius.circular(5.0),
                                  side: BorderSide(color: Colors.transparent)),
                              color: Theme.of(context).accentColor,
                              splashColor: Colors.amberAccent,
                              textColor: const Color(0xFFFFFFFF),
                              icon: Icon(Icons.close),
                              label: Text('Закрыть'),
                              onPressed: () {
                                Navigator.of(context).pop();
                              },
                            )
                          ],
                        );
                      });
                }
              },
              tooltip: 'Отправить ответ',
              backgroundColor: Colors.green,
              icon: Icon(Icons.check)),
        ));
  }
}

Ответы (1 шт):

Автор решения: MiT

Покажу на более легком примере.

Как было:

MyWidget(),

...

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text("123"), // !!!ОТЛИЧАЕТСЯ!!!
    );
  }
}

Стало:

MyWidget("123")

...

class MyWidget extends StatefulWidget {
  final String text;

  MyWidget(this.text);

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

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(widget.text),
    );
  }
}

Что произошло: мы добавили поле, которое инициализируется в конструкторе класса. Данное поле обязательно должно быть final. И получаем данные через widget.

Также бывает, что нам нужно инициализировать не все поля, для этого можно сделать так:

MyWidget("123") // или так MyWidget("123", number: 1)

...

class MyWidget extends StatefulWidget {
  final int number;
  final String text;

  MyWidget(this.text, {this.number});

  @override
  _MyWidgetState createState() => _MyWidgetState();
}
→ Ссылка