为什么我的Flutter页面有时不能在发布版本中完全呈现?

5lwkijsr  于 2022-12-24  发布在  Flutter
关注(0)|答案(5)|浏览(176)

问题

我有一个Flutter应用程序,它有一个登录页面。当我在调试模式下运行应用程序时,当应用程序打开时,登录页面会正确呈现。但是当我用flutter build apk --release构建应用程序的apk版本,安装它,然后在模拟器中打开应用程序时,登录页面不会正确呈现。请参见下面的截图。

在调试模式下运行,正确呈现:

运行发布版本,未完全呈现:

代码

登录页面代码:

// @dart=2.9

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
// import 'package:myapp/helpers/FCMHelper.dart';
import 'package:myapp/helpers/SecureStorageHelper.dart';
import 'package:myapp/helpers/SharedPreferencesHelper.dart';
import 'package:myapp/models/LoginModel.dart';
import 'package:http/http.dart' as http;
import 'package:myapp/models/Token.dart';
import 'package:myapp/globals.dart' as globals;
import 'package:myapp/models/UserAndTokenModel.dart';
import 'package:myapp/pages/home_page.dart';
import 'package:myapp/pages/reset_password_page.dart';
import 'package:swipedetector/swipedetector.dart';
import 'package:url_launcher/url_launcher.dart';

import '../home_page.dart';

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();
  String loginResponse = "";
  SecureStorageHelper secureStorage;
  bool rememberMe;
  bool isFirstBuild;
  bool isKeyboardVisible = false;
  double displayWidth;
  FocusNode emailFocus;
  FocusNode passwordFocus;
  KeyboardVisibilityController keyboardVisibilityController;

  @override
  void initState() {
    init();
    delayedInit();
    super.initState();
  }

  init() {
    emailFocus = new FocusNode();
    passwordFocus = new FocusNode();
    displayWidth = 1;
    rememberMe = false;
    addKeyBoardListener();
    secureStorage = new SecureStorageHelper();
  }

  delayedInit() {
    Future.delayed(Duration.zero, () {
      getReemberMe();
      tryFillUserCredentials();
      tryAutoLogin();
    });
  }

  getReemberMe() async {
    rememberMe =
        await SharedPreferencesHelper.getBool("myapp_remember_me");
    setState(() {
      rememberMe = rememberMe ?? false;
    });
  }

  tryFillUserCredentials() async {
    var username = await secureStorage.get("myapp_email");
    var password = await secureStorage.get("myapp_password");
    setState(() {
      _usernameController.text = username;
      _passwordController.text = password;
    });
  }

  @override
  Widget build(BuildContext context) {
    hideSystemOverlay();
    setDisplayDimensions();
    return SwipeDetector(
        onSwipeDown: onSwipeDown,
        child: GestureDetector(
          onTap: onScaffoldTap,
          child: Scaffold(
            resizeToAvoidBottomInset: true,
            body: SafeArea(
              child: Stack(
                children: [
                  buildControlsLayer(),
                  buildPolicyLayer(),
                  buildVersionLayer()
                ],
              ),
            ),
          ),
        ));
  }

  buildLogo(int inFlex) {
    return Flexible(
      flex: inFlex,
      child: Image.asset(
        'assets/myapp_logo.png',
        height: MediaQuery.of(context).size.height * 0.20,
      ),
    );
  }

  rememberMeChange(bool newValue) {
    setState(() {
      rememberMe = newValue;
    });
  }

  privacyPolicyTapped() {
    resetFocus();
    launch("https://www.myteam.se/policy.html");
  }

  void resetPasswordPressed() {
    resetFocus();
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => ResetPasswordPage()),
    );
  }

  clearUserCredentialTextfields() {
    setState(() {
      _usernameController.clear();
      _passwordController.clear();
    });
  }

  void loggaInPressed() async {
    resetFocus();
    SharedPreferencesHelper.setBool("myapp_remember_me", rememberMe);
    if (!rememberMe)
      deleteUserCredentials();
    else
      storeUserCredentials(_usernameController.text, _passwordController.text);
    var loginModel =
        new LoginModel(_usernameController.text, _passwordController.text);
    var response0 = await postLogin(loginModel);
    if (response0.statusCode == 200) {
      if (!rememberMe) clearUserCredentialTextfields();
      setState(() {
        loginResponse = "";
      });
      var loginToken = Token.fromJson(json.decode(response0.body));
      globals.loginToken = loginToken;
      var response1 = await getUserToken(loginToken.token);
      if (response1.statusCode == 200) {
        var userAndToken =
            UserAndTokenModel.fromJson(json.decode(response1.body));
        var userToken = new Token(token: userAndToken.token);
        var userModel = userAndToken.userModel;
        globals.userToken = userToken;
        globals.userModel = userModel;
        if (rememberMe) storeTokens(loginToken.token, userToken.token);
        // var fcmHelper = new FCMHelper();
        // fcmHelper.configureFCM(context);
        // fcmHelper.registerFCMToken(userModel);
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => HomePage()),
        );
      } else
        setState(() {
          loginResponse = "Fel uppstod vid inloggning.";
        });
    } else
      setState(() {
        loginResponse = "Felaktig email eller lösenord.";
      });
  }

  storeUserCredentials(String email, String password) {
    secureStorage.set("myapp_email", email);
    secureStorage.set("myapp_password", password);
  }

  storeTokens(String loginToken, String userToken) {
    secureStorage.set("myapp_login_token", loginToken);
    secureStorage.set("myapp_user_token", userToken);
  }

  deleteUserCredentials() {
    secureStorage.delete("myapp_email");
    secureStorage.delete("myapp_password");
    secureStorage.delete("myapp_login_token");
    secureStorage.delete("myapp_user_token");
  }

  Future<http.Response> postLogin(LoginModel loginModel) {
    String data = json.encode(loginModel);
    return http.post(
        Uri.parse(globals.apiBaseUrl + "authentication/authenticate_mobile"),
        body: data,
        headers: {
          'Content-type': 'application/json',
          'Accept': 'application/json'
        });
  }

  Future<http.Response> postFCMToken(String data) {
    var userToken = globals.userToken.token;
    return http.post(Uri.parse(globals.apiBaseUrl + "/user/set_fcm_token"),
        body: data,
        headers: {
          'Content-type': 'application/json',
          'Accept': 'application/json',
          "Authorization": "Bearer $userToken"
        });
  }

  Future<http.Response> getUserToken(String loginToken) {
    return http
        .get(Uri.parse(globals.apiBaseUrl + "user/get_by_token"), headers: {
      'Content-type': 'application/json',
      'Accept': 'application/json',
      "Authorization": "Bearer $loginToken"
    });
  }

  tryAutoLogin() async {
    var loginToken = await secureStorage.get("myapp_login_token");
    if (loginToken != null) autoLogin();
  }

  autoLogin() async {
    var loginToken = await secureStorage.get("myapp_login_token");
    var response = await getUserToken(loginToken);
    if (response.statusCode == 200) {
      var userAndToken = UserAndTokenModel.fromJson(json.decode(response.body));
      var userToken = new Token(token: userAndToken.token);
      var userModel = userAndToken.userModel;
      globals.userToken = userToken;
      globals.userModel = userModel;
      // var fcmHelper = new FCMHelper();
      // fcmHelper.configureFCM(context);
      // fcmHelper.registerFCMToken(userModel);
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => HomePage()),
      );
    } else
      showMessageDialog(
          "Inloggningssessionen utgången", "Logga in igen med dina uppgifter.");
  }

  void showMessageDialog(String title, String body) {
    try {
      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: new Text(title),
              content: new Text(body),
            );
          });
    } catch (e) {
      print(e.toString());
    }
  }

  void usernameChange(String value) {
    print("");
  }

  void addKeyBoardListener() {
    keyboardVisibilityController = KeyboardVisibilityController();
    keyboardVisibilityController.onChange.listen(onKeyboardVisibilityChange);
  }

  Widget buildUsernameTextfieldAdaptedToKeyboard() {
    return isKeyboardVisible
        ? buildKeyBoardVisibleUsernameTextfield()
        : buildKeyBoardHiddenUsernameTextfield();
  }

  Widget buildPasswordTextfieldAdaptedToKeyboard() {
    return isKeyboardVisible
        ? buildKeyboardVisiblePasswordTextfield()
        : buildKeyboardHiddenPasswordTextfield();
  }

  Widget buildKeyBoardVisibleUsernameTextfield() {
    return Expanded(
      flex: 18,
      child: Container(
        child: Row(
          children: <Widget>[
            Spacer(
              flex: 1,
            ),
            Flexible(
              flex: 8,
              child: TextField(
                  focusNode: emailFocus,
                  controller: _usernameController,
                  autocorrect: false,
                  keyboardType: TextInputType.emailAddress,
                  maxLengthEnforcement: MaxLengthEnforcement.none,
                  onChanged: usernameChange,
                  style: TextStyle(
                      height: 1,
                      fontSize: displayWidth * globals.textFieldFontSize0),
                  decoration: InputDecoration(
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0)),
                    filled: true,
                    fillColor: Colors.white,
                    contentPadding: EdgeInsets.all(
                        MediaQuery.of(context).size.height * 0.02),
                    labelText: 'Epost',
                  ),
                  textCapitalization: TextCapitalization.none),
            ),
            Spacer(
              flex: 1,
            )
          ],
        ),
      ),
    );
  }

  Widget buildKeyBoardHiddenUsernameTextfield() {
    return Expanded(
      flex: 8,
      child: Container(
        child: Row(
          children: <Widget>[
            Spacer(
              flex: 1,
            ),
            Flexible(
              flex: 8,
              child: TextField(
                  focusNode: emailFocus,
                  controller: _usernameController,
                  autocorrect: false,
                  keyboardType: TextInputType.emailAddress,
                  maxLengthEnforcement: MaxLengthEnforcement.none,
                  onChanged: usernameChange,
                  style: TextStyle(
                      height: 1,
                      fontSize: displayWidth * globals.textFieldFontSize0),
                  decoration: InputDecoration(
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0)),
                    filled: true,
                    fillColor: Colors.white,
                    contentPadding: EdgeInsets.all(
                        MediaQuery.of(context).size.height * 0.02),
                    labelText: 'Epost',
                  ),
                  textCapitalization: TextCapitalization.none),
            ),
            Spacer(
              flex: 1,
            )
          ],
        ),
      ),
    );
  }

  Widget buildKeyboardVisiblePasswordTextfield() {
    return Flexible(
      flex: 18,
      child: Row(
        children: <Widget>[
          Spacer(
            flex: 1,
          ),
          Flexible(
            flex: 8,
            child: TextField(
                focusNode: passwordFocus,
                maxLengthEnforcement: MaxLengthEnforcement.none,
                controller: _passwordController,
                autocorrect: false,
                style: TextStyle(
                    height: 1,
                    fontSize: displayWidth * globals.textFieldFontSize0),
                decoration: InputDecoration(
                  contentPadding:
                      EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
                  border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10.0)),
                  filled: true,
                  fillColor: Colors.white,
                  labelText: 'Lösenord',
                ),
                obscureText: true,
                textCapitalization: TextCapitalization.none),
          ),
          Spacer(
            flex: 1,
          )
        ],
      ),
    );
  }

  Widget buildKeyboardHiddenPasswordTextfield() {
    return Flexible(
      flex: 8,
      child: Row(
        children: <Widget>[
          Spacer(
            flex: 1,
          ),
          Flexible(
            flex: 8,
            child: TextField(
                focusNode: passwordFocus,
                maxLengthEnforcement: MaxLengthEnforcement.none,
                controller: _passwordController,
                autocorrect: false,
                style: TextStyle(
                    height: 1,
                    fontSize: displayWidth * globals.textFieldFontSize0),
                decoration: InputDecoration(
                  contentPadding:
                      EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
                  border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10.0)),
                  filled: true,
                  fillColor: Colors.white,
                  labelText: 'Lösenord',
                ),
                obscureText: true,
                textCapitalization: TextCapitalization.none),
          ),
          Spacer(
            flex: 1,
          )
        ],
      ),
    );
  }

  Widget buildKeyboardAdaptedResponseForms() {
    return Expanded(
      flex: isKeyboardVisible ? 6 : 5,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Spacer(
            flex: 1,
          ),
          Expanded(
            flex: 8,
            child: Container(
                child: Text("$loginResponse",
                    overflow: TextOverflow.visible,
                    style: new TextStyle(
                        fontSize: displayWidth * globals.linkFontSize0,
                        color: Colors.red),
                    textAlign: TextAlign.left)),
          ),
          Spacer(
            flex: 1,
          )
        ],
      ),
    );
  }

  Widget buildKeyboardAdaptedRememberMeForms() {
    return isKeyboardVisible
        ? Container()
        : Expanded(
            flex: 3,
            child: Row(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Spacer(
                  flex: 1,
                ),
                Expanded(
                  flex: 8,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.start,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisSize: MainAxisSize.max,
                    children: <Widget>[
                      Container(
                        child: Text(
                          "Kom ihåg mig",
                          overflow: TextOverflow.visible,
                          style: TextStyle(
                              fontSize: displayWidth * globals.linkFontSize0),
                        ),
                      ),
                      Container(
                        child: Checkbox(
                          value: rememberMe,
                          onChanged: rememberMeChange,
                        ),
                      )
                    ],
                  ),
                ),
                Spacer(
                  flex: 1,
                )
              ],
            ),
          );
  }

  Widget buildKeyboardAdaptedIForgotPasswordLink() {
    return isKeyboardVisible
        ? Container()
        : Flexible(
            flex: 5,
            child: Row(
              mainAxisSize: MainAxisSize.max,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                Spacer(
                  flex: 1,
                ),
                Expanded(
                  flex: 8,
                  child: Container(
                    alignment: Alignment.centerLeft,
                    child: InkWell(
                      child: Text(
                        "Jag har glömt mitt lösenord",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            decoration: TextDecoration.underline,
                            fontSize: displayWidth * 0.04),
                      ),
                      onTap: resetPasswordPressed,
                    ),
                  ),
                ),
                Spacer(
                  flex: 1,
                )
              ],
            ),
          );
  }

  buildKeyboardAdaptedButtonBar() {
    return Flexible(
        flex: isKeyboardVisible ? 14 : 6,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Spacer(
              flex: 4,
            ),
            Expanded(
                flex: 15,
                child: ElevatedButton(
                  style: ButtonStyle(
                      backgroundColor: MaterialStateColor.resolveWith(
                          (states) => Color.fromRGBO(217, 217, 217, 1))),
                  child: Text(
                    'Logga in',
                    textAlign: TextAlign.center,
                    style: new TextStyle(
                        fontSize: displayWidth * globals.buttonFontSize0,
                        color: new Color.fromRGBO(54, 104, 129, 1.0)),
                  ),
                  onPressed: loggaInPressed,
                )),
            Spacer(
              flex: 1,
            ),
            Expanded(
              flex: 15,
              child: ElevatedButton(
                style: ButtonStyle(
                    backgroundColor: MaterialStateColor.resolveWith(
                        (states) => Color.fromRGBO(217, 217, 217, 1))),
                child: Text(
                  'Avbryt',
                  style: new TextStyle(
                      fontSize: displayWidth * globals.buttonFontSize0,
                      color: new Color.fromRGBO(54, 104, 129, 1.0)),
                ),
                onPressed: () {
                  resetFocus();
                  _usernameController.clear();
                  _passwordController.clear();
                },
              ),
            ),
            Spacer(
              flex: 4,
            ),
          ],
        ));
  }

  void setDisplayDimensions() {
    if (displayWidth == 1) displayWidth = MediaQuery.of(context).size.width;
  }

  void onKeyboardVisibilityChange(bool visible) {
    setState(() {
      isKeyboardVisible = visible;
    });
  }

  hideSystemOverlay() {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
  }

  void onScaffoldTap() {
    resetFocus();
  }

  void resetFocus() {
    try {
      if (emailFocus.hasFocus) emailFocus.unfocus();
      if (passwordFocus.hasFocus) passwordFocus.unfocus();
      if (passwordFocus.hasFocus || emailFocus.hasFocus)
        Focus.of(context).unfocus();
    } catch (e) {}
  }

  onSwipeDown() {
    resetFocus();
  }

  buildPolicyLink() {
    return InkWell(
      child: Text(
        "Sekretesspolicy",
        style: TextStyle(
            fontSize: displayWidth * globals.linkFontSize0,
            color: Colors.blue,
            decoration: TextDecoration.underline),
      ),
      onTap: privacyPolicyTapped,
    );
  }

  buildVersionNr() {
    return Text(
      "Version 1.2.9",
      style: TextStyle(
          fontSize: displayWidth * globals.linkFontSize0, color: Colors.black),
    );
  }

  buildControlsLayer() {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Spacer(flex: isKeyboardVisible ? 2 : 7),
          buildLogo(isKeyboardVisible ? 40 : 20),
          Spacer(flex: isKeyboardVisible ? 1 : 3),
          Flexible(
            flex: 10,
            child: Text(
              "Välkommen till My App",
              overflow: TextOverflow.visible,
              style: new TextStyle(
                  fontSize: displayWidth * globals.titleFontSIze0),
              textAlign: TextAlign.center,
            ),
          ),
          Spacer(
            flex: 7,
          ),
          buildUsernameTextfieldAdaptedToKeyboard(),
          Spacer(flex: isKeyboardVisible ? 4 : 2),
          buildPasswordTextfieldAdaptedToKeyboard(),
          isKeyboardVisible ? Spacer(flex: 2) : Container(),
          buildKeyboardAdaptedResponseForms(),
          buildKeyboardAdaptedRememberMeForms(),
          buildKeyboardAdaptedIForgotPasswordLink(),
          Spacer(flex: isKeyboardVisible ? 4 : 2),
          buildKeyboardAdaptedButtonBar(),
          Spacer(
            flex: isKeyboardVisible ? 2 : 10,
          )
        ],
      ),
    );
  }

  buildPolicyLayer() {
    return isKeyboardVisible
        ? Container()
        : Positioned(
            bottom: 10,
            left: 10,
            child: buildPolicyLink(),
          );
  }

  buildVersionLayer() {
    return isKeyboardVisible
        ? Container()
        : Positioned(bottom: 10, right: 10, child: buildVersionNr());
  }
}

公共质量标准名称:

name: myapp
description: A new Flutter project.

version: 1.0.0+1

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  http: ^0.13.4
  flutter:
    sdk: flutter
  firebase_messaging: ^11.0.0
  intl: ^0.17.0
  shared_preferences: ^2.0.8
  path_provider_platform_interface: ^2.0.1
  platform: ^3.0.2
  flutter_secure_storage: ^4.2.1
  swipedetector: ^1.2.0

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.3
  url_launcher: ^6.0.3
  firebase_core: ^1.0.4
  flutter_keyboard_visibility: ^5.1.0

dev_dependencies:
  flutter_test:
    sdk: flutter

# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true
  assets:
    - assets/myapp_logo.png
    - assets/myapp_logo.png
    - assets/logo_small.png
    - assets/green_smiley.png
    - assets/orange_smiley.png
    - assets/red_smiley.png
    - assets/horizontal_line.png

最后的想法和问题
为什么登录页面在发布版本中没有正确呈现,而是在调试模式下工作?
谢谢!

qfe3c7zg

qfe3c7zg1#

这可能是因为渲染运行时出现了错误(异常),你检查过是否有异常吗?例如,如果你正在使用Sentry这样的错误报告工具,请到它的网页上查看。如果你没有,请尝试查看日志。或者,按照官方指南设置错误处理(例如,简单地打印它):https://flutter.dev/docs/testing/errors.
如果你找不到任何线索,尝试设置错误处理,并把所有的日志在这里,我可以尝试看到它。

    • 编辑**

有了更多的信息,我可以解释发生了什么。

void setDisplayDimensions() {
    if (displayWidth == 1) displayWidth = MediaQuery.of(context).size.width;
  }

  void buildOtherPartsOfUI() {
   ...use displayWidth...
  }

这似乎不是一个完美的方法。如果setDisplayDimensions在buildOtherPartsOfUI之后被调用,UI将使用旧的宽度而不是新获取的宽度呈现。它将只在下一个build发生时更新。然而,更糟糕的是,您无法控制下一个构建发生的时间。(您可以使用setState让Flutter知道它应该在下一帧中调用build;但由于您没有这样做,Flutter可以在以后调用build,或者如果UI没有更改,它可以在100年后调用build。)

wrrgggsh

wrrgggsh2#

我也有一个类似的实现,就是尽早获取屏幕显示信息,计算比率并将其存储到一个静态字段中,这个静态字段用于让应用对设备的显示做出响应。
它在调试模式下工作正常,但在发布模式下无法正确呈现内容。
截图如下:
| 调试模式|发布模式|
| - ------|- ------|
|

|

|

根本原因

在发布模式下,应用不需要加载所有调试功能,因此轻量级应用的加载速度更快。
在Flutter 2.10.3中,它无法在构建应用程序的早期获得正确的显示信息,而且如果该值被存储并在以后重用,它将无法按预期工作。

解决方案(有点古怪......)

我在runApp()之前添加了一个延迟任务,就像我们在Flutter应用程序中初始化Firebase一样,这将为Flutter引擎完成设置预留足够的时间。
这也解决了我的另一个问题。启动画面只显示在一个 Flink 在释放模式,我希望用户可以看到正确的标志。

启动应用程序的默认主函数:

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

解决方案-在runApp()之前添加延迟任务:

void main() async {
  /* 
    * Additional function call to ensure widgets flutter binding is initialized,
    * but the issue still happens if no delay task below.
  */
  WidgetsFlutterBinding.ensureInitialized();
  /* 
    * This will make splash screen lasts for the duration of the delay task.
    * 300ms looks well for me, can adjust based on your preference.
  */
  await Future.delayed(const Duration(milliseconds: 300));
  runApp(const MyApp());
}

具体实施请参考this repository

ttcibm8c

ttcibm8c3#

发现问题

现在我发现了错误。问题是我如何使用MediaQuery.of(context).size.width。我首先调用setDisplayWidth()方法来获得显示宽度,然后将该值赋给dispayWidth变量。但由于某种原因,这在发布模式下不起作用。然而,由于setDisplayWidth()调用没有按我的预期工作,因此小部件实际上被呈现了。所有文本的大小都设置为0 - 1像素,这使得文本/小部件看起来没有正确呈现。因为您可以在问题中的代码中看到我是如何相对于displayWidth变量设置所有文本大小的。但是displayWidth变量的默认值始终为1,这使得文本实际上不可见。
为了解决上述问题,我完全删除了displayWidth变量,而是在每次需要显示宽度时直接在build方法中获取MediaQuery.of(context).size.width

新代码(解决方案)

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
// import 'package:inflecto/helpers/FCMHelper.dart';
import 'package:inflecto/helpers/SecureStorageHelper.dart';
import 'package:inflecto/helpers/SharedPreferencesHelper.dart';
import 'package:inflecto/models/LoginModel.dart';
import 'package:http/http.dart' as http;
import 'package:inflecto/models/Token.dart';
import 'package:inflecto/globals.dart' as globals;
import 'package:inflecto/models/UserAndTokenModel.dart';
import 'package:inflecto/pages/home_page.dart';
import 'package:inflecto/pages/reset_password_page.dart';
import 'package:swipedetector/swipedetector.dart';
import 'package:url_launcher/url_launcher.dart';

import '../home_page.dart';

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();
  String loginResponse = "";
  SecureStorageHelper secureStorage = new SecureStorageHelper();
  bool rememberMe = false;
  bool isKeyboardVisible = false;
  FocusNode emailFocus = new FocusNode();
  FocusNode passwordFocus = new FocusNode();
  KeyboardVisibilityController keyboardVisibilityController =
      new KeyboardVisibilityController();

  @override
  void initState() {
    init();
    delayedInit();
    super.initState();
  }

  init() {
    emailFocus = new FocusNode();
    passwordFocus = new FocusNode();
    rememberMe = false;
    addKeyBoardListener();
    secureStorage = new SecureStorageHelper();
  }

  delayedInit() {
    Future.delayed(Duration.zero, () async {
      await getReemberMe();
      await tryFillUserCredentials();
      await tryAutoLogin();
    });
  }

  getReemberMe() async {
    rememberMe =
        await SharedPreferencesHelper.getBool("hjarnkraft_remember_me") ??
            false;
    setState(() {
      rememberMe = rememberMe;
    });
  }

  tryFillUserCredentials() async {
    var username = await secureStorage.get("hjarnkraft_email");
    var password = await secureStorage.get("hjarnkraft_password");
    setState(() {
      _usernameController.text = username ?? "";
      _passwordController.text = password ?? "";
    });
  }

  @override
  Widget build(BuildContext context) {
    hideSystemOverlay();
    return SwipeDetector(
        onSwipeDown: onSwipeDown,
        child: GestureDetector(
          onTap: onScaffoldTap,
          child: Scaffold(
            resizeToAvoidBottomInset: true,
            body: SafeArea(
              child: Stack(
                children: [
                  buildControlsLayer(),
                  buildPolicyLayer(),
                  buildVersionLayer()
                ],
              ),
            ),
          ),
        ));
  }

  buildLogo(int inFlex) {
    return Flexible(
      flex: inFlex,
      child: Image.asset(
        'assets/hjarnkraft_logo.png',
        height: MediaQuery.of(context).size.height * 0.20,
      ),
    );
  }

  rememberMeChange(bool? newValue) {
    setState(() {
      rememberMe = newValue ?? false;
    });
  }

  privacyPolicyTapped() {
    resetFocus();
    launch("https://www.knowe.se/policy.html");
  }

  void resetPasswordPressed() {
    resetFocus();
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => ResetPasswordPage()),
    );
  }

  clearUserCredentialTextfields() {
    setState(() {
      _usernameController.clear();
      _passwordController.clear();
    });
  }

  void loggaInPressed() async {
    resetFocus();
    SharedPreferencesHelper.setBool("hjarnkraft_remember_me", rememberMe);
    if (!rememberMe)
      deleteUserCredentials();
    else
      storeUserCredentials(_usernameController.text, _passwordController.text);
    var loginModel =
        new LoginModel(_usernameController.text, _passwordController.text);
    var response0 = await postLogin(loginModel);
    if (response0.statusCode == 200) {
      if (!rememberMe) clearUserCredentialTextfields();
      setState(() {
        loginResponse = "";
      });
      var loginToken = Token.fromJson(json.decode(response0.body));
      globals.loginToken = loginToken;
      var response1 = await getUserToken(loginToken.token);
      if (response1.statusCode == 200) {
        var userAndToken =
            UserAndTokenModel.fromJson(json.decode(response1.body));
        var userToken = new Token(token: userAndToken.token);
        var userModel = userAndToken.userModel;
        globals.userToken = userToken;
        globals.userModel = userModel;
        if (rememberMe) storeTokens(loginToken.token, userToken.token);
        // var fcmHelper = new FCMHelper();
        // fcmHelper.configureFCM(context);
        // fcmHelper.registerFCMToken(userModel);
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => HomePage()),
        );
      } else
        setState(() {
          loginResponse = "Fel uppstod vid inloggning.";
        });
    } else
      setState(() {
        loginResponse = "Felaktig email eller lösenord.";
      });
  }

  storeUserCredentials(String email, String password) {
    secureStorage.set("hjarnkraft_email", email);
    secureStorage.set("hjarnkraft_password", password);
  }

  storeTokens(String loginToken, String userToken) {
    secureStorage.set("hjarnkraft_login_token", loginToken);
    secureStorage.set("hjarnkraft_user_token", userToken);
  }

  deleteUserCredentials() {
    secureStorage.delete("hjarnkraft_email");
    secureStorage.delete("hjarnkraft_password");
    secureStorage.delete("hjarnkraft_login_token");
    secureStorage.delete("hjarnkraft_user_token");
  }

  Future<http.Response> postLogin(LoginModel loginModel) {
    String data = json.encode(loginModel);
    return http.post(
        Uri.parse(globals.apiBaseUrl + "authentication/authenticate_mobile"),
        body: data,
        headers: {
          'Content-type': 'application/json',
          'Accept': 'application/json'
        });
  }

  Future<http.Response> postFCMToken(String data) {
    var userToken = globals.userToken.token;
    return http.post(Uri.parse(globals.apiBaseUrl + "/user/set_fcm_token"),
        body: data,
        headers: {
          'Content-type': 'application/json',
          'Accept': 'application/json',
          "Authorization": "Bearer $userToken"
        });
  }

  Future<http.Response> getUserToken(String? loginToken) {
    return http
        .get(Uri.parse(globals.apiBaseUrl + "user/get_by_token"), headers: {
      'Content-type': 'application/json',
      'Accept': 'application/json',
      "Authorization": "Bearer $loginToken"
    });
  }

  tryAutoLogin() async {
    var loginToken = await secureStorage.get("hjarnkraft_login_token");
    if (loginToken != null) autoLogin();
  }

  autoLogin() async {
    var loginToken = await secureStorage.get("hjarnkraft_login_token");
    var response = await getUserToken(loginToken);
    if (response.statusCode == 200) {
      var userAndToken = UserAndTokenModel.fromJson(json.decode(response.body));
      var userToken = new Token(token: userAndToken.token);
      var userModel = userAndToken.userModel;
      globals.userToken = userToken;
      globals.userModel = userModel;
      // var fcmHelper = new FCMHelper();
      // fcmHelper.configureFCM(context);
      // fcmHelper.registerFCMToken(userModel);
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => HomePage()),
      );
    } else
      showMessageDialog(
          "Inloggningssessionen utgången", "Logga in igen med dina uppgifter.");
  }

  void showMessageDialog(String title, String body) {
    try {
      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: new Text(title),
              content: new Text(body),
            );
          });
    } catch (e) {
      print(e.toString());
    }
  }

  void usernameChange(String value) {
    print("");
  }

  void addKeyBoardListener() {
    keyboardVisibilityController = KeyboardVisibilityController();
    keyboardVisibilityController.onChange.listen(onKeyboardVisibilityChange);
  }

  Widget buildUsernameTextfieldAdaptedToKeyboard() {
    return isKeyboardVisible
        ? buildKeyBoardVisibleUsernameTextfield()
        : buildKeyBoardHiddenUsernameTextfield();
  }

  Widget buildPasswordTextfieldAdaptedToKeyboard() {
    return isKeyboardVisible
        ? buildKeyboardVisiblePasswordTextfield()
        : buildKeyboardHiddenPasswordTextfield();
  }

  Widget buildKeyBoardVisibleUsernameTextfield() {
    return Expanded(
      flex: 18,
      child: Container(
        child: Row(
          children: <Widget>[
            Spacer(
              flex: 1,
            ),
            Flexible(
              flex: 8,
              child: TextField(
                  focusNode: emailFocus,
                  controller: _usernameController,
                  autocorrect: false,
                  keyboardType: TextInputType.emailAddress,
                  maxLengthEnforcement: MaxLengthEnforcement.none,
                  onChanged: usernameChange,
                  style: TextStyle(
                      height: 1,
                      fontSize: MediaQuery.of(context).size.width *
                          globals.textFieldFontSize0),
                  decoration: InputDecoration(
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0)),
                    filled: true,
                    fillColor: Colors.white,
                    contentPadding: EdgeInsets.all(
                        MediaQuery.of(context).size.height * 0.02),
                    labelText: 'Epost',
                  ),
                  textCapitalization: TextCapitalization.none),
            ),
            Spacer(
              flex: 1,
            )
          ],
        ),
      ),
    );
  }

  Widget buildKeyBoardHiddenUsernameTextfield() {
    return Expanded(
      flex: 8,
      child: Container(
        child: Row(
          children: <Widget>[
            Spacer(
              flex: 1,
            ),
            Flexible(
              flex: 8,
              child: TextField(
                  focusNode: emailFocus,
                  controller: _usernameController,
                  autocorrect: false,
                  keyboardType: TextInputType.emailAddress,
                  maxLengthEnforcement: MaxLengthEnforcement.none,
                  onChanged: usernameChange,
                  style: TextStyle(
                      height: 1,
                      fontSize: MediaQuery.of(context).size.width *
                          globals.textFieldFontSize0),
                  decoration: InputDecoration(
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0)),
                    filled: true,
                    fillColor: Colors.white,
                    contentPadding: EdgeInsets.all(
                        MediaQuery.of(context).size.height * 0.02),
                    labelText: 'Epost',
                  ),
                  textCapitalization: TextCapitalization.none),
            ),
            Spacer(
              flex: 1,
            )
          ],
        ),
      ),
    );
  }

  Widget buildKeyboardVisiblePasswordTextfield() {
    return Flexible(
      flex: 18,
      child: Row(
        children: <Widget>[
          Spacer(
            flex: 1,
          ),
          Flexible(
            flex: 8,
            child: TextField(
                focusNode: passwordFocus,
                maxLengthEnforcement: MaxLengthEnforcement.none,
                controller: _passwordController,
                autocorrect: false,
                style: TextStyle(
                    height: 1,
                    fontSize: MediaQuery.of(context).size.width *
                        globals.textFieldFontSize0),
                decoration: InputDecoration(
                  contentPadding:
                      EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
                  border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10.0)),
                  filled: true,
                  fillColor: Colors.white,
                  labelText: 'Lösenord',
                ),
                obscureText: true,
                textCapitalization: TextCapitalization.none),
          ),
          Spacer(
            flex: 1,
          )
        ],
      ),
    );
  }

  Widget buildKeyboardHiddenPasswordTextfield() {
    return Flexible(
      flex: 8,
      child: Row(
        children: <Widget>[
          Spacer(
            flex: 1,
          ),
          Flexible(
            flex: 8,
            child: TextField(
                focusNode: passwordFocus,
                maxLengthEnforcement: MaxLengthEnforcement.none,
                controller: _passwordController,
                autocorrect: false,
                style: TextStyle(
                    height: 1,
                    fontSize: MediaQuery.of(context).size.width *
                        globals.textFieldFontSize0),
                decoration: InputDecoration(
                  contentPadding:
                      EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
                  border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(10.0)),
                  filled: true,
                  fillColor: Colors.white,
                  labelText: 'Lösenord',
                ),
                obscureText: true,
                textCapitalization: TextCapitalization.none),
          ),
          Spacer(
            flex: 1,
          )
        ],
      ),
    );
  }

  Widget buildKeyboardAdaptedResponseForms() {
    return Expanded(
      flex: isKeyboardVisible ? 6 : 5,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Spacer(
            flex: 1,
          ),
          Expanded(
            flex: 8,
            child: Container(
                child: Text("$loginResponse",
                    overflow: TextOverflow.visible,
                    style: new TextStyle(
                        fontSize: MediaQuery.of(context).size.width *
                            globals.linkFontSize0,
                        color: Colors.red),
                    textAlign: TextAlign.left)),
          ),
          Spacer(
            flex: 1,
          )
        ],
      ),
    );
  }

  Widget buildKeyboardAdaptedRememberMeForms() {
    return isKeyboardVisible
        ? Container()
        : Expanded(
            flex: 3,
            child: Row(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Spacer(
                  flex: 1,
                ),
                Expanded(
                  flex: 8,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.start,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisSize: MainAxisSize.max,
                    children: <Widget>[
                      Container(
                        child: Text(
                          "Kom ihåg mig",
                          overflow: TextOverflow.visible,
                          style: TextStyle(
                              fontSize: MediaQuery.of(context).size.width *
                                  globals.linkFontSize0),
                        ),
                      ),
                      Container(
                        child: Checkbox(
                          value: rememberMe,
                          onChanged: rememberMeChange,
                        ),
                      )
                    ],
                  ),
                ),
                Spacer(
                  flex: 1,
                )
              ],
            ),
          );
  }

  Widget buildKeyboardAdaptedIForgotPasswordLink() {
    return isKeyboardVisible
        ? Container()
        : Flexible(
            flex: 5,
            child: Row(
              mainAxisSize: MainAxisSize.max,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                Spacer(
                  flex: 1,
                ),
                Expanded(
                  flex: 8,
                  child: Container(
                    alignment: Alignment.centerLeft,
                    child: InkWell(
                      child: Text(
                        "Jag har glömt mitt lösenord",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            decoration: TextDecoration.underline,
                            fontSize: MediaQuery.of(context).size.width * 0.04),
                      ),
                      onTap: resetPasswordPressed,
                    ),
                  ),
                ),
                Spacer(
                  flex: 1,
                )
              ],
            ),
          );
  }

  buildKeyboardAdaptedButtonBar() {
    return Flexible(
        flex: isKeyboardVisible ? 14 : 6,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Spacer(
              flex: 4,
            ),
            Expanded(
                flex: 15,
                child: ElevatedButton(
                  style: ButtonStyle(
                      backgroundColor: MaterialStateColor.resolveWith(
                          (states) => Color.fromRGBO(217, 217, 217, 1))),
                  child: Text(
                    'Logga in',
                    textAlign: TextAlign.center,
                    style: new TextStyle(
                        fontSize: MediaQuery.of(context).size.width *
                            globals.buttonFontSize0,
                        color: new Color.fromRGBO(54, 104, 129, 1.0)),
                  ),
                  onPressed: loggaInPressed,
                )),
            Spacer(
              flex: 1,
            ),
            Expanded(
              flex: 15,
              child: ElevatedButton(
                style: ButtonStyle(
                    backgroundColor: MaterialStateColor.resolveWith(
                        (states) => Color.fromRGBO(217, 217, 217, 1))),
                child: Text(
                  'Avbryt',
                  style: new TextStyle(
                      fontSize: MediaQuery.of(context).size.width *
                          globals.buttonFontSize0,
                      color: new Color.fromRGBO(54, 104, 129, 1.0)),
                ),
                onPressed: () {
                  resetFocus();
                  _usernameController.clear();
                  _passwordController.clear();
                },
              ),
            ),
            Spacer(
              flex: 4,
            ),
          ],
        ));
  }

  void onKeyboardVisibilityChange(bool visible) {
    setState(() {
      isKeyboardVisible = visible;
    });
  }

  hideSystemOverlay() {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
  }

  void onScaffoldTap() {
    resetFocus();
  }

  void resetFocus() {
    try {
      if (emailFocus.hasFocus) emailFocus.unfocus();
      if (passwordFocus.hasFocus) passwordFocus.unfocus();
      if (passwordFocus.hasFocus || emailFocus.hasFocus)
        Focus.of(context).unfocus();
    } catch (e) {}
  }

  onSwipeDown() {
    resetFocus();
  }

  buildPolicyLink() {
    return InkWell(
      child: Text(
        "Sekretesspolicy",
        style: TextStyle(
            fontSize: MediaQuery.of(context).size.width * globals.linkFontSize0,
            color: Colors.blue,
            decoration: TextDecoration.underline),
      ),
      onTap: privacyPolicyTapped,
    );
  }

  buildVersionNr() {
    return Text(
      "Version 1.2.9",
      style: TextStyle(
          fontSize: MediaQuery.of(context).size.width * globals.linkFontSize0,
          color: Colors.black),
    );
  }

  buildControlsLayer() {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Spacer(flex: isKeyboardVisible ? 2 : 7),
          buildLogo(isKeyboardVisible ? 40 : 20),
          Spacer(flex: isKeyboardVisible ? 1 : 3),
          Flexible(
            flex: 10,
            child: Text(
              "Välkommen till Hjärnkraft",
              overflow: TextOverflow.visible,
              style: new TextStyle(
                  fontSize: MediaQuery.of(context).size.width *
                      globals.titleFontSIze0),
              textAlign: TextAlign.center,
            ),
          ),
          Spacer(
            flex: 7,
          ),
          buildUsernameTextfieldAdaptedToKeyboard(),
          Spacer(flex: isKeyboardVisible ? 4 : 2),
          buildPasswordTextfieldAdaptedToKeyboard(),
          isKeyboardVisible ? Spacer(flex: 2) : Container(),
          buildKeyboardAdaptedResponseForms(),
          buildKeyboardAdaptedRememberMeForms(),
          buildKeyboardAdaptedIForgotPasswordLink(),
          Spacer(flex: isKeyboardVisible ? 4 : 2),
          buildKeyboardAdaptedButtonBar(),
          Spacer(
            flex: isKeyboardVisible ? 2 : 10,
          )
        ],
      ),
    );
  }

  buildPolicyLayer() {
    return isKeyboardVisible
        ? Container()
        : Positioned(
            bottom: 10,
            left: 10,
            child: buildPolicyLink(),
          );
  }

  buildVersionLayer() {
    return isKeyboardVisible
        ? Container()
        : Positioned(bottom: 10, right: 10, child: buildVersionNr());
  }
}

结论

在你的应用程序和小工具中使用MediaQuery.of(context).size.width时要小心。总是测试发布版本,因为它的行为可能与调试版本完全不同。我不知道为什么我的第一个代码不能正常工作。

um6iljoc

um6iljoc4#

我知道这个问题已经回答了,但对我来说,问题是灵活的组件,我用错了,如果其他答案对你没有帮助,看看这个组件文档,并检查它是否适合你的项目。
PS:在调试模式下我没有问题,只有在发布模式下。

dbf7pr2w

dbf7pr2w5#

我遇到了这个问题。小部件在调试模式下工作正常,但在发布模式下没有显示。在我的情况下,问题已经通过修复Incorrect use of ParentDataWidget警告解决了。

相关问题