flutter - 文本字段光标无法正常工作

标签 flutter

当我从数据库中检索数据并将其显示在文本字段中然后尝试对其进行编辑时,文本字段光标无法正常工作。

每次返回更改方法的验证结果时,我都希望光标的位置得到尊重。

How its working

完整代码如下。

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TextField Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  TextEditingController _nameController = TextEditingController();
  TextEditingController _emailController = TextEditingController();
  ApplicationBloc _bloc;

  @override
  void initState() {
    super.initState();
    _bloc = new ApplicationBloc();
    _bloc.initData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TextField Test'),
      ),
      body: ListView(
        shrinkWrap: true,
        children: <Widget>[_buildName(), _buildEmail(), _buildSubmit()],
      ),
    );
  }

  Widget _buildName() {
    return StreamBuilder(
      stream: _bloc.name,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          _nameController.text = snapshot.data;
        }
        return TextField(
          controller: _nameController,
          onChanged: _bloc.changeName,
          keyboardType: TextInputType.text,
          decoration: InputDecoration(
            labelText: "Name",
            errorText: snapshot.error,
          ),
        );
      },
    );
  }

  Widget _buildEmail() {
    return StreamBuilder(
      stream: _bloc.email,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          _emailController.text = snapshot.data;
        }
        return TextField(
          controller: _emailController,
          onChanged: _bloc.changeEmail,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
            labelText: "Email",
            errorText: snapshot.error,
          ),
        );
      },
    );
  }

  Widget _buildSubmit() {
    return StreamBuilder(
      stream: _bloc.submit,
      builder: (context, snapshot) {
        return RaisedButton(
          onPressed: (!snapshot.hasData || !snapshot.data)
              ? null
              : () {
                  _bloc.submitForm();
                  _nameController.text = '';
                  _emailController.text = '';
                },
          child: Text('Submit!'),
        );
      },
    );
  }
}

class ApplicationBloc {
  BehaviorSubject<String> _nameController =
      BehaviorSubject<String>(seedValue: '');
  Observable<String> get name => _nameController.stream.transform(validateName);
  Function(String) get changeName => _nameController.sink.add;

  BehaviorSubject<String> _emailController =
      BehaviorSubject<String>(seedValue: '');
  Observable<String> get email =>
      _emailController.stream.transform(validateEmail);
  Function(String) get changeEmail => _emailController.sink.add;

  Observable<bool> get submit => Observable.combineLatest2(
      name, email, (e, e1) => e.isNotEmpty && e1.isNotEmpty);

  initData() {
    // This data from database
    _nameController.sink.add('Test123');
    _emailController.sink.add('test@email.com');
  }    

  submitForm() {
    //Send to api and wait
    //Reset values
    Future.delayed(const Duration(seconds: 1));
  }

  final validateName =
      StreamTransformer<String, String>.fromHandlers(handleData: (name, sink) {
    if (name.isEmpty || name.length > 4) {
      sink.add(name);
    } else if (name.isNotEmpty) {
      sink.addError('Invalid Name!');
    }
  });

  final validateEmail =
      StreamTransformer<String, String>.fromHandlers(handleData: (email, sink) {
    String p =
        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';

    RegExp regExp = new RegExp(p);
    if (email.isEmpty || (email.length > 4 && regExp.hasMatch(email))) {
      sink.add(email);
    } else {
      sink.addError('Invalid email!');
    }
  });

  //dispose/close all the streams when we call dispose() method
  void dispose() {
    _nameController.close();
    _emailController.close();
  }
}

我尝试了一个将光标位置更改为最终位置的代码,但是当我尝试编辑文本字段的中间内容时它无法正常工作。

最佳答案

如果您查看 TextEditingControllertext setter,您会看到:

set text(String newText) {
  value = value.copyWith(
    text: newText,
    selection: const TextSelection.collapsed(offset: -1),
    composing: TextRange.empty,
  );
}

请注意,selection 已重置,因此您无法使用此 setter 。相反,在您的构建器中,执行:

if (snapshot.hasData) {
  _nameController.value = _nameController.value.copyWith(
    text: snapshot.data,
  );
}

只是文本会发生变化,您不必担心其他属性。

关于flutter - 文本字段光标无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56140310/

相关文章:

android - 为什么我的小部件的构建方法认为我返回 NULL?

android - 找不到方法实现 Flutter Android 应用

ios - iOS 上的共享首选项

flutter - 是否可以在 Flutter 上查看我们之前的导航页面?

flutter - Flutter:如何仅使用其中一个子项覆盖整个专栏

dart - 来自Json的Flutter Dart Sticky Header

java - 如何修复错误 :processDebugMainManifest DEBUG

android - Flutter - initState() 中的复杂函数会降低导航和性能

android - 滚动轮播、默认选项卡 Controller 和 GridView 在 flutter 中合而为一

flutter - 仅在 dart/flutter 中取十进制数