Flutter:流在Android虚拟设备上提供了零值,但在真实的Android手机上可以正常工作

时间:2019-05-04 13:34:20

标签: android flutter avd

在带有Android Studio的Android虚拟设备上的flutter应用程序中,StreamControllers出现了一些非常奇怪的行为。当我连接真正的android设备时,该应用程序运行正常。

我正在使用一个用于身份验证表单的块,该表单在更改时侦听,验证值,然后在提交时将其发送到服务器。

我遇到的问题是,电子邮件和密码均已正确添加到流中,并且正在按预期进行验证。

但是,如果我碰到的最后一个字段是密码,则在SubmitForm()中单击Submit时,_email.value的值为null。如果我最后触摸的字段是电子邮件,则_password.value的值为null。同样,这仅在AVD中发生。当我使用真实的电话时,_email和_paswsword的值都符合预期。

我完全删除了Android Studio,然后重新安装,但结果是相同的。我在使用docker时正在运行Hyper V和Windows Hypervisor平台,但是Android并没有关于HAMX的警告。

更新:

我刚刚安装了genymotion,它也可以正常工作,因此似乎是Windows上的AVD问题。我仍然希望AVD能够正常运行,因为精灵运动无法在Hyper V上运行。

系统:

OS Name Microsoft Windows 10 Pro
Version 10.0.17763 Build 17763
Other OS Description    Not Available
OS Manufacturer Microsoft Corporation
System Manufacturer MSI
System Model    MS-7A68
System Type x64-based PC
System SKU  Default string
Processor   Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz, 3600 Mhz, 4 Core(s), 8 Logical Processor(s)
BIOS Version/Date   American Megatrends Inc. 1.30, 28/06/2017
SMBIOS Version  3.0
Embedded Controller Version 255.255
BIOS Mode   UEFI
BaseBoard Manufacturer  MSI
BaseBoard Product   Z270 TOMAHAWK (MS-7A68)
BaseBoard Version   1.0
Platform Role   Desktop
Secure Boot State   Off
PCR7 Configuration  Binding Not Possible
Windows Directory   C:\WINDOWS
System Directory    C:\WINDOWS\system32
Boot Device \Device\HarddiskVolume3
Locale  United States
Hardware Abstraction Layer  Version = "10.0.17763.404"
Time Zone   GMT Daylight Time
Installed Physical Memory (RAM) 16.0 GB
Total Physical Memory   16.0 GB
Available Physical Memory   4.49 GB
Total Virtual Memory    21.5 GB
Available Virtual Memory    5.44 GB
Page File Space 5.50 GB
Page File   C:\pagefile.sys
Kernel DMA Protection   Off
Virtualization-based security   Running
Virtualization-based security Required Security Properties  
Virtualization-based security Available Security Properties Base Virtualization Support, UEFI Code Readonly, Mode Based Execution Control
Virtualization-based security Services Configured   
Virtualization-based security Services Running  
Device Encryption Support   Reasons for failed automatic device encryption: TPM is not usable, PCR7 binding is not supported, Hardware Security Test Interface failed and device is not InstantGo, Un-allowed DMA capable bus/device(s) detected, TPM is not usable
A hypervisor has been detected. Features required for Hyper-V will not be displayed.    

代码:

bloc:

import 'dart:async';
import 'dart:convert';
import 'package:rxdart/rxdart.dart';
import 'package:http/http.dart' as http;
import './auth_validator.dart';

class AuthBloc with AuthValidator {
  final _email = BehaviorSubject<String>();
  final _password = BehaviorSubject<String>();
  final _isLoading = BehaviorSubject<bool>();
  final _loginSucceded = BehaviorSubject<bool>();

  // stream getters
  Stream<String> get email => _email.stream.transform(validateEmail);
  Stream<String> get password => _password.stream.transform(validatePassword);
  Stream<bool> get isLoading => _isLoading.stream;
  Stream<bool> get loginSuccess => _loginSucceded.stream;
  Stream<Map> get redirect => Observable.combineLatest2(_loginSucceded,
      _isLoading, (log, load) => {'loginSuccess': log, 'isLoading': load});
  Stream<bool> get submitValid =>
      Observable.combineLatest2(email, password, (e, p) => true);

  // add data to sink onChange
  Function(String) get emailChanged => _email.sink.add;
  Function(String) get passwordChanged => _password.sink.add;

  login(jsonUser) async {
    try {
      // submit to server
      final http.Response response = await http.post(
        'http://192.168.1.213:5000/api/users/signin',
        body: jsonUser,
        headers: {'Content-Type': 'application/json'},
      );

      final Map<String, dynamic> decodedRes = await json.decode(response.body);

      if (response.statusCode == 200) {
        _loginSucceded.sink.add(true);
      }

      return decodedRes;
    } catch (e) {
      _isLoading.sink.add(false);
    }
  }

  void submitForm() async {
    try {
      final Map user = {
        'email': _email.value,
        'password': _password.value
      };
      final jsonUser = json.encode(user);
      print(jsonUser);

      await login(jsonUser);

      void dispose() {
        _email.close();
        _password.close();
        _isLoading.close();
        _loginSucceded.close();
      }
    } catch (e) {
      print('error: $e');
    }
  }
}

验证者:

import 'dart:async';

class AuthValidator {
  final validateEmail = StreamTransformer<String, String>.fromHandlers(handleData: (email, sink) {
    if (!email.contains(RegExp(r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"))) {
      sink.addError('Please enter a valid email address');
    } else {
      sink.add(email);
    }
  });

  final validatePassword = StreamTransformer<String, String>.fromHandlers(handleData: (password, sink) {
      final goodLength = password.length >= 8;
      final hasNumber = password.contains(RegExp(r'[0-9]'));

      if (!goodLength) {
        sink.addError('Must be at least 8 characters and contains a number');
      } else {
        sink.add(password);
      }
    });
}

页面:

import 'package:flutter/material.dart';
import '../blocs/auth_bloc.dart';

class LoginPage extends StatelessWidget {
  final authBloc = AuthBloc();

  @override
  Widget build(BuildContext context) {
    // TODO redirect would not work in StreamBuilder block
    // manually listening for redirect conidtions here
    // once a response is received from the server
    authBloc.loginSuccess.listen((data) {
      if (data) {
        Navigator.pushReplacementNamed(context, '/dashboard');
      }
    });

    return StreamBuilder(
        stream: authBloc.isLoading,
        initialData: false,
        builder: (context, snapshot) {
          print(snapshot.data);
          return Scaffold(
            body: _buildPage(authBloc, snapshot.data),
          );
        });
  }

  Widget _buildPage(AuthBloc authBloc, bool isLoading) {
    return Container(
      margin: EdgeInsets.all(20.0),
      child: Center(
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              _emailField(authBloc),
              _padding(),
              _passwordField(authBloc),
              _padding(),
              _submitButton(authBloc, isLoading)
            ],
          ),
        ),
      ),
    );
  }

  Widget _circularSpinner() {
    return Center(
      child: CircularProgressIndicator(),
    );
  }

  Widget _emailField(AuthBloc authBloc) {
    return StreamBuilder(
      stream: authBloc.email,
      builder: (BuildContext context, snapshot) {
        return Container(
          padding: EdgeInsets.only(top: 10.0),
          child: TextField(
            onChanged: authBloc.emailChanged,
            autofocus: true,
            keyboardType: TextInputType.emailAddress,
            decoration: InputDecoration(
              hintText: 'you@example.com',
              labelText: 'Email Address',
              errorText: snapshot.error,
              border: OutlineInputBorder(),
            ),
          ),
        );
      },
    );
  }

  Widget _passwordField(AuthBloc authBloc) {
    return StreamBuilder(
      stream: authBloc.password,
      builder: (BuildContext context, snapshot) {
        return TextField(
          onChanged: authBloc.passwordChanged,
          obscureText: true,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
            hintText: '8 characters or more with at least 1 number',
            labelText: 'Password',
            errorText: snapshot.error,
            border: OutlineInputBorder(),
          ),
        );
      },
    );
  }

  Widget _padding() {
    return Padding(
      padding: EdgeInsets.only(top: 20.0),
    );
  }

  Widget _submitButton(AuthBloc authBloc, isLoading) {
    final spinner = CircularProgressIndicator();
    final button =  RaisedButton(
            child: Text('Login'),
            color: Colors.blue,
            onPressed: authBloc.submitForm,
          );
    return StreamBuilder(
        stream: authBloc.submitValid,
        builder: (context, snapshot) {
         return isLoading ? spinner : button;
        });
  }
}

0 个答案:

没有答案