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

时间:2019-05-15 00:32:39

标签: flutter

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

我希望每次返回change方法的验证结果时都要尊重光标的位置。

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();
  }
}

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

1 个答案:

答案 0 :(得分:2)

如果您查看text的{​​{1}}设置器,则会看到:

TextEditingController

请注意,set text(String newText) { value = value.copyWith( text: newText, selection: const TextSelection.collapsed(offset: -1), composing: TextRange.empty, ); } 已重设,因此您不能使用此设置器。相反,在您的构建器中,执行以下操作:

selection

只需更改文本,您就不必担心其他属性。