简体   繁体   English

如何在 Flutter/Dart 的同一屏幕中保存和加载两个变量?

[英]How to save and load two variables in the same screen in Flutter/Dart?

Here is my code:这是我的代码:

import 'package:flutter/material.dart';
import 'package:csv/csv.dart';
import 'package:flutter/services.dart' show rootBundle;

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Example';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
        home: Container(
            child: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        body: const MyStatelessWidget(),
      ),
    )
    );
  }
}

class MyStatelessWidget extends StatelessWidget {
  const MyStatelessWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          const SizedBox(height: 30),
          ClipRRect(
            borderRadius: BorderRadius.circular(4),
            child: Stack(
              children: <Widget>[
                Positioned.fill(
                  child: Container(
                    decoration: const BoxDecoration(
                      gradient: LinearGradient(
                        colors: <Color>[
                          Color(0xFF0D47A1),
                          Color(0xFF1976D2),
                          Color(0xFF42A5F5),
                        ],
                      ),
                    ),
                  ),
                ),
                TextButton(
                  style: TextButton.styleFrom(
                    padding: const EdgeInsets.all(16.0),
                    primary: Colors.white,
                    textStyle: const TextStyle(fontSize: 20),
                  ),
                  onPressed: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(builder: (context) => const NewGameRoute()),
                    );
                  },
                  child: const Text('New Game'),
                ),
              ],
            ),
          ),
          const SizedBox(height: 30),
          ClipRRect(
            borderRadius: BorderRadius.circular(4),
            child: Stack(
              children: <Widget>[
                Positioned.fill(
                  child: Container(
                    decoration: const BoxDecoration(
                      gradient: LinearGradient(
                        colors: <Color>[
                          Color(0xFF0D47A1),
                          Color(0xFF1976D2),
                          Color(0xFF42A5F5),
                        ],
                      ),
                    ),
                  ),
                ),
                TextButton(
                  style: TextButton.styleFrom(
                    padding: const EdgeInsets.all(16.0),
                    primary: Colors.white,
                    textStyle: const TextStyle(fontSize: 20),
                  ),
                  onPressed: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(builder: (context) => const NewGameRoute()),
                    );
                  },
                  child: const Text('Continue Game'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}


// New Game route
class NewGameRoute extends StatelessWidget {
  const NewGameRoute({key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'New Game',
      home: ListFromCSV(),
    );
  }
}

class ListFromCSV extends StatefulWidget {
  const ListFromCSV({Key? key}) : super(key: key);

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

class _ListFromCSVState extends State<ListFromCSV> {
  List<List<dynamic>> _listData = [
    [""]
  ];
  int _listCount = 0;
  bool _isFirstLoad = true;
  String assetPath = "files/main.jpg";

  @override
  void initState() {
    _loadCSV();
  }

  // This function is only triggered at init, so we only load csv once
  void _loadCSV() async {
    String rawData = await rootBundle.loadString("files/Text.csv");
    _listData = const CsvToListConverter().convert(rawData);
    assetPath = _listData[_listCount][1] == ""
        ? "files/main.jpg"
        : _listData[_listCount][1];
  }

  // This function is triggered when my button is pressed
  void _nextCSV() {
    setState(() {
      _listData = _listData;
      _listCount < _listData.length - 1
          ? _isFirstLoad
          ? _isFirstLoad = false
          : _listCount++
          : _listCount;
      assetPath =
      _listData[_listCount][1] == "" ? assetPath : _listData[_listCount][1];
    });
  }

  // This function makes buttons visible/invisible
  bool isVisible = true; //will be visible for the first frame

  void _isVisible() {
    setState(() {
      isVisible = !isVisible;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('New Game'),
      ),
      body: Container(
        height: MediaQuery.of(context).size.height,
        decoration: BoxDecoration(
            image: DecorationImage(
                image: AssetImage(assetPath),
                fit: BoxFit.cover)),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Visibility(
              child: Column(
              children: [
                ClipRRect(
                  borderRadius: BorderRadius.circular(4),
                  child: Stack(
                    children: <Widget>[
                      Positioned.fill(
                        child: Container(
                          decoration: BoxDecoration(
                              image: DecorationImage(
                                  image: AssetImage('files/sheet.jpg'),
                                  fit: BoxFit.cover)),
                        ),
                      ),
                      Text(_listData[_listCount][0]),
                    ],
                  ),
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    ImageButton(label: 'OK', onButtonTap: _nextCSV),
                    ImageButton(label: 'Hide', onButtonTap: _isVisible),
                  ],
                ),
              ],
            ),
              visible: isVisible,
            ),
            // your other widgets
            Visibility(
              child: ImageButton(label: 'Show', onButtonTap: _isVisible),
              visible: !isVisible,
            )

          ],
        ),
      ),
    );
  }
}


//Class for a cool button
class ImageButton extends StatelessWidget {
  const ImageButton({Key? key, required this.label, required this.onButtonTap})
      : super(key: key);
  final String label;
  final Function onButtonTap;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () => onButtonTap(),
      child: Container(
        // customize you button shape and size and design
        margin: const EdgeInsets.all(8),
        padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 32),
        decoration: const BoxDecoration(
            borderRadius: BorderRadius.all(Radius.circular(2)),
            image: DecorationImage(
                image: AssetImage("files/sheet.jpg"), // you can also pass the image dynamically with variable created for the widget.
                fit: BoxFit.cover)),
        child: Center(
          child: Text(
            label,
            style: const TextStyle(
                color: Colors
                    .black, // you can get dominant colour on image and change the text color accordingly or apply shadows to the text
                fontWeight: FontWeight.w500,
                fontSize: 16),
          ),
        ),
      ),
    );
  }
}

This experimental app consists of two screens:这个实验性应用由两个屏幕组成:

Main screen:主屏幕:

在此处输入图像描述

Game screen:游戏画面:

在此处输入图像描述

I need to save 2 variables: _listCount and assetPath so that later on my users can continue the game.我需要保存 2 个变量:_listCount 和assetPath,以便以后我的用户可以继续游戏。 But at the same time, the New Game and Continue Game screens are almost identical.但与此同时,New Game 和 Continue Game 屏幕几乎完全相同。 In fact, this is the same screen.其实这是同一个画面。 The only difference is that when my user starts the game, the variables have the following values:唯一的区别是当我的用户开始游戏时,变量具有以下值:

int_listCount = 0;
String assetPath = "files/main.jpg";

And when my user clicks on the "Continue game" button, they should get to the same screen, but the values of the variables should be different.当我的用户点击“继续游戏”按钮时,他们应该进入同一个屏幕,但变量的值应该不同。 What is the easiest way to do this?最简单的方法是什么?

I believe I should follow these steps:我相信我应该遵循以下步骤:

  1. Save the _listCount and assetPath variables when I click the OK button to the device's memory.当我单击 OK 按钮时,将 _listCount 和 assetPath 变量保存到设备的内存中。 To do this, I need to modify this code:为此,我需要修改此代码:
      void _nextCSV() {
        setState(() {
          _listData = _listData;
          _listCount < _listData.length - 1
              ? _isFirstLoad
              ? _isFirstLoad = false
              : _listCount++
              : _listCount;
          assetPath =
          _listData[_listCount][1] == "" ? assetPath : _listData[_listCount][1];
        });
      }
  1. Load the values of the _listCount and assetPath variables from the device's memory when I click the Continue game button.当我单击继续游戏按钮时,从设备内存中加载 _listCount 和assetPath 变量的值。 To do this, I need to modify this code:为此,我需要修改此代码:
    onPressed: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(builder: (context) => const NewGameRoute()),
                        );
                      },

That is, the user will go to NewGameRoute, but with different values for these variables.也就是说,用户将转到 NewGameRoute,但这些变量的值不同。 Or would it be correct to create a ContinueGameRoute that is almost exactly the same as NewGameRoute (other than my values)?或者创建一个与 NewGameRoute 几乎完全相同(除了我的值)的 ContinueGameRoute 是否正确? What is the easiest way to save and load these values from memory?从内存中保存和加载这些值的最简单方法是什么?

Edit 1. Using Sparsh Jain 's code, I have the following:编辑 1.使用Sparsh Jain的代码,我有以下内容:

Launching lib\main.dart on sdk gphone64 x86 64 in debug mode...
Running Gradle task 'assembleDebug'...
lib/main.dart:168:7: Error: Expected an identifier, but got ':'.
Try inserting an identifier before ':'.
      : super(key: key);
      ^
lib/main.dart:168:24: Error: Expected '}' before this.
      : super(key: key);
                       ^
lib/main.dart:172:3: Error: Expected '{' before this.
  final int _listCount;
  ^^^^^
lib/main.dart:75:23: Error: No named parameter with the name '_listCount'.
                      _listCount: 0, assetPath: "images/01.jpg" ///What I want to pass
                      ^^^^^^^^^^
lib/main.dart:164:3: Context: Found this candidate, but the arguments don't match.
  NewGameRoute(
  ^^^^^^^^^^^^
lib/main.dart:85:69: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
Try using a constructor or factory that is 'const'.
                      MaterialPageRoute(builder: (context) => const NewGameRoute()),
                                                                    ^^^^^^^^^^^^
lib/main.dart:168:20: Error: Undefined name 'key'.
      : super(key: key);
                   ^^^
lib/main.dart:168:9: Error: Method invocation is not a constant expression.
      : super(key: key);
        ^^^^
lib/main.dart:172:13: Error: Final field '_listCount' is not initialized.
Try to initialize the field in the declaration or in every constructor.
  final int _listCount;
            ^^^^^^^^^^
lib/main.dart:174:16: Error: Final field 'assetPath' is not initialized.
Try to initialize the field in the declaration or in every constructor.
  final String assetPath;
               ^^^^^^^^^


FAILURE: Build failed with an exception.

* Where:
Script 'D:\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 1156

* What went wrong:
Execution failed for task ':app:compileFlutterBuildDebug'.
> Process 'command 'D:\flutter\bin\flutter.bat'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 9s
Exception: Gradle task assembleDebug failed with exit code 1

Edit 2. With the help of more experienced people I modified this code yesterday.编辑 2.在更有经验的人的帮助下,我昨天修改了这段代码。 Now it looks like this and has only one error.现在它看起来像这样,并且只有一个错误。 It still doesn't work, though.但是,它仍然不起作用。

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Example';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
        home: Container(
            child: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        // body: const MyStatelessWidget(),
              body: const MainWidget(),
      ),
    )
    );
  }
}

// class MyStatelessWidget extends StatelessWidget {
//   const MyStatelessWidget({Key? key}) : super(key: key);
class MainWidget extends StatefulWidget {
  const MainWidget({Key? key}) : super(key: key);

  @override
  State<MainWidget> createState() => _MainWidgetState();
}


class _MainWidgetState extends State<MainWidget> {
  CheckUserConnection _checkUserConnection = CheckUserConnection();
  InternetDialogHandler _internetDialogHandler = InternetDialogHandler();
  bool? _internetAvailable;

  @override
  void initState(){
    checkNet();
    super.initState();
  }


  void checkNet() async{
    _internetAvailable = await
    _checkUserConnection.checkInternetAvailability();
    setState((){});
  }



  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Column(
            children: [
              GradientButton(label: 'New Game', onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const NewGameRoute(
                      listCount: 0, assetPath: "images/01.jpg" ///What I want to pass


                  )),
                );
              }),
              // GradientButton(label: 'Continue Game', onTap: () {
              //   return _internetAvailable == true ?
              //   {Navigator.push(
              //         context,
              //         MaterialPageRoute(builder: (context) => const NewGameRoute()),
              //       )}
              //   :
              //   _internetDialogHandler.showInternetDialog(context);
              // }),
            ],
          ),
          Column(
            children: [
              GradientButton(label: 'Back Button', onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const BackRoute()),
                );



                // print('Button 1');



              }),
              GradientButton(label: 'Button 2', onTap: () {print('Button 2');}),
              GradientButton(label: 'Internet', onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const InternetRoute()),
                );
              }),
            ],
          )
        ],
      ),
    );
  }
}


//Class for a gradient button
class GradientButton extends StatelessWidget {
  const GradientButton({Key? key, required this.label, required this.onTap}) : super(key: key);
  final String label;
  final Function onTap;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 15),
      child: GestureDetector(
        onTap: () => onTap(),
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
          decoration: const BoxDecoration(
            borderRadius: BorderRadius.all(Radius.circular(4)),
            gradient: LinearGradient(
              colors: <Color>[
                Color(0xFF0D47A1),
                Color(0xFF1976D2),
                Color(0xFF42A5F5),
              ],
            ),
          ),
          child: Text(label, style: const TextStyle(fontSize: 20, color: Colors.white, fontWeight: FontWeight.normal, decoration: TextDecoration.none),),
        ),
      ),
    );
  }
}


// New Game route
  class NewGameRoute extends StatelessWidget {
    const NewGameRoute({
      Key? key,
      required int listCount,
      required this.assetPath,
    })  : _listCount = listCount,
          super(key: key);

    final int _listCount;

    final String assetPath;


  @override
  Widget build(BuildContext context) { // removed materialApp from here
    return ListFromCSV();
  }
}


class ListFromCSV extends StatefulWidget {
  final int listCount;
  const ListFromCSV({
    Key? key,
    required this.listCount,
  }) : super(key: key);

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

class _ListFromCSVState extends State<ListFromCSV> {
  List<List<dynamic>> _listData = [
    [""]
  ];
  late int _listCount = widget.listCount;




  // This function is only triggered at init, so we only load csv once
  void _loadCSV() async {
    String rawData = await rootBundle.loadString("files/Text.csv");
    _listData = const CsvToListConverter().convert(rawData);
  }



  // This function is triggered when my button is pressed
  void _nextCSV() {
    setState(() {
      _listData = _listData;
      _listCount < _listData.length - 1
          ? _isFirstLoad
          ? _isFirstLoad = false
          : _listCount++
          : _listCount;
      // assetPath =
      // _listData[_listCount][1] == "" ? assetPath : _listData[_listCount][1];
      _listData[_listCount][1] == "" ? null : _showAlertDialog();
    });
  }

  // This function makes buttons visible/invisible
  bool isVisible = true; //will be visible for the first frame

  void _isVisible() {
    setState(() {
      isVisible = !isVisible;
    });
  }


//Alert Dialog about questions and answers
  Widget _answer1TextButton(){
    return TextButton(
      child: Text(_listData[_listCount][3]),
      onPressed:  () {
        setState(() {
        assetPath = _listData[_listCount][6];
        _listCount = _listData[_listCount][2]-1;
        // _listData[_listCount][0];
        // _nextCSV();
        print('Answer 1');
        print(_listCount);
        // Navigator.of(globalContext).pop();  // Popping globalContext
          });
      },
    );
  }
  Widget _answer2TextButton(){
    return TextButton(
      child: Text(_listData[_listCount][5]),
      onPressed:  () {
        setState(() {
        assetPath = _listData[_listCount][7];
        _listCount = _listData[_listCount][4]-1;
        print('Answer 2');
        print(_listCount);
        // Navigator.of(globalContext).pop();  // Popping globalContext
        });
        },
    );
  }

  void _showAlertDialog() {

// set up the AlertDialog
    AlertDialog alert = AlertDialog(
      // title: Text(),
      content: Text(_listData[_listCount][1]),
      actions: [
        _answer1TextButton(),
        _answer2TextButton(),
      ],
    );

// show the dialog
    showDialog(
      barrierDismissible: false,  //use to dismiss any tap on the background of the dialog
      context: context,
      // useRootNavigator: false, //this property needs to be added
      builder: (BuildContext context) {
        return WillPopScope(
          onWillPop: () async {
            return true;   // false to disable the back button
          },
          child: alert,
        );
      },
    );
  }



  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('New Game'),
        ),
        body: Container(
          height: MediaQuery.of(context).size.height,
          decoration: BoxDecoration(
              image: DecorationImage(
                  image: AssetImage(assetPath),
                  fit: BoxFit.cover)),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Visibility(
                child: Column(
                  children: [
                    ClipRRect(
                      borderRadius: BorderRadius.circular(4),
                      child: Stack(
                        children: <Widget>[
                          Positioned.fill(
                            child: Container(
                              decoration: BoxDecoration(
                                  image: DecorationImage(
                                      image: AssetImage('files/sheet.jpg'),
                                      fit: BoxFit.cover)),
                            ),
                          ),
                          Text(_listData[_listCount][0]),
                        ],
                      ),
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: [
                        ImageButton(label: 'OK', onButtonTap: _nextCSV),
                        ImageButton(label: 'Hide', onButtonTap: _isVisible),
                        ImageButton(label: 'Test1', onButtonTap: _showAlertDialog),
                      ],
                    ),
                  ],
                ),
                visible: isVisible,
              ),
              // your other widgets
              Visibility(
                child: ImageButton(label: 'Show', onButtonTap: _isVisible),
                visible: !isVisible,
              )

            ],
          ),
        ),
      );
  }
}


//Class for a cool button
class ImageButton extends StatelessWidget {
  const ImageButton({Key? key, required this.label, required this.onButtonTap})
      : super(key: key);
  final String label;
  final Function onButtonTap;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () => onButtonTap(),
      child: Container(
        // customize you button shape and size and design
        margin: const EdgeInsets.all(8),
        padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 32),
        decoration: const BoxDecoration(
            borderRadius: BorderRadius.all(Radius.circular(2)),
            image: DecorationImage(
                image: AssetImage("files/sheet.jpg"), // you can also pass the image dynamically with variable created for the widget.
                fit: BoxFit.cover)),
        child: Center(
          child: Text(
            label,
            style: const TextStyle(
                color: Colors
                    .black, // you can get dominant colour on image and change the text color accordingly or apply shadows to the text
                fontWeight: FontWeight.w500,
                fontSize: 16),
          ),
        ),
      ),
    );
  }
}

Now I only get one error.现在我只得到一个错误。

The named parameter 'assetPath' is required, but there's no corresponding argument.

It leads to它导致

@override
   Widget build(BuildContext context) {
     return ListFromCSV();
   }
}

What could be the reason?可能是什么原因?

Edit 3. Here's what my code was missing:编辑 3.这是我的代码丢失的内容:

@override
  Widget build(BuildContext context) {
    return ListFromCSV(listCount: _listCount, assetPath: assetPath);
  }

Now it works perfect.现在它完美无缺。

You can pass the value of variables from the base page to the NewGameRoute class.您可以将variables的值从基本页面传递给NewGameRoute类。

can call it like, NewGameRoute(10, "abc.csv");可以这样称呼它, NewGameRoute(10, "abc.csv");

class NewGameRoute extends StatelessWidget {
  NewGameRoute(
      {Key? key,
      required this. _listCount,
      required this. assetPath,
      : super(key: key);

  final int _listCount;

  final String assetPath;


  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'New Game',
      home: ListFromCSV(),
    );
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM