美文网首页Flutter学习Flutter
Flutter中实现Autofill(Android)&Keyc

Flutter中实现Autofill(Android)&Keyc

作者: 天上飘的是浮云 | 来源:发表于2021-11-20 14:29 被阅读0次

    为什么要写这篇文章,第一是做了这个功能,记录一下,为后来者提供思路和解决方案;第二是因为我们走了弯路,我们探了雷,所以为后来者提点注意事项;O(∩_∩)O哈哈~

    某日,前沿战地提来需求,需要在登录页面添加密码自动填充,自动保存,还可以编辑删除功能。预研一下,并尽快评估一下开发耗时。

    后方地勤人员接到命令后,火速赶往pub.dev。找出了最优的库,并demo试之。发现可行(仅仅能填入密码,没覆盖保存和删除功能),反馈于前沿征地。

    一、开干

    至于那个库就不说了,后面发现前面没测保存和删除功能,而也没有去研究Flutter TextFiled自带的功能(PS: 其实它是自带Autofill功能的,这后面再说)

    然后,直接从原生入手,想着原生可以实现的,只要把原生的View写好,功能实现,在Flutter端直接引用原生UI就行了。这倒好,这又开始学了一把Flutter调用显示原生UI的技术(后边再写一文,讲讲),但是iOS实现了,Android这边出了问题,单个显示原生EditText时可以弹出输入框,展示多个EditText时候,输入框都弹不出来。

    试了好多办法,单个的EditText不行,用LinearLayout或者其他FrameLayout包裹也不行,实在找不到原因了。就开始Google,却发现了新大陆。

    二、撕开迷雾,初见光明

    最开始呢,其他同学先做了iOS原生方式实现了KeyChain功能,想着Android也得做原生方式实现Autofill自动填充功能。只能交给我这根独苗苗来了,但是实际过程中,发现flutter调用原生视图方式输入框起不来。只能另寻他法了。

    皇天不负有心人,让我发现了新大陆,欣喜若狂~

    参考链接如下:
    1. 自动填充框架(Android)
    2. Level up your Flutter apps with autofill
    3. How to implement autofill in your Flutter app
    4. 其实最有用的是这个 -> Flutter Tutorial - Autofill Services In 5 Minutes - Android & iOS

    前面的1. 自动填充框架(Android)是让我们能了解下Android端的自动填充是个什么玩意儿,原生端是怎么玩的。这对于我们在Flutter端去使用和开发Autofill功能是很有帮助的。

    4. 其实最有用的是这个 -> Flutter Tutorial - Autofill Services In 5 Minutes - Android & iOS这是最有用的,这是个GitHub上的一个Example,具体从哪找的,忘了。

    三、Flutter端的开发

    在Flutter端如果要实现KeyChain和Autofill功能,其实很简单。Flutter早已帮你实现,我们是纯属想多O(∩_∩)O哈哈~。

    3.1 如果说我们要实现账户名密码的自动填充,提示登录成功后弹出保存按钮,只需要实现下面代码:
    1. AutofillGroup
    2. autofillHints: [AutofillHints.username]
    3. TextInput.finishAutofillContext()
    
      Widget buildUsernameAndPassword() => AutofillGroup(
            child: Column(
              children: [
                TextField(
                  controller: usernameController,
                  decoration: InputDecoration(labelText: 'Username'),
                  keyboardType: TextInputType.name,
                  textInputAction: TextInputAction.next,
                  autofillHints: [AutofillHints.username],
                ),
                TextField(
                  controller: passwordController,
                  decoration: InputDecoration(labelText: 'Password'),
                  keyboardType: TextInputType.visiblePassword,
                  autofillHints: [AutofillHints.password],
                  onEditingComplete: () => TextInput.finishAutofillContext(),
                ),
              ],
            ),
          );
    

    这里需要注意的是:

      1. 两个TextField需要被AutofillGroup包裹,它是将AutofillClient(这里是指TextField)进行组合在一起的Widget,这些被AutofillGroup包裹的AutofillClients需要一块构建,它们将被一起填充,比如说,点了用户名,提示账户名密码后,选择后会自动将用户名和密码输入框都填充好。
    如果你保存过该App的账户密码,那么点击输入框的时候就会弹出存储的信息供你选择 选择某个账号信息后,因为是使用的AutofillGroup包裹,那它将会自动将用户名和密码输入框都自动填充
      1. 看到上面代码中TextField里的autofillHints: [AutofillHints.username]和autofillHints: [AutofillHints.password]这里就和iOS、Android原生类似了。在原生中: Android是在EditText属性中加入android:autofillHints="username";而iOS则是在textField.textContentType = UITextContentTypeUsername或UITextContentTypePassword;
      1. AutofillHints除了AutofillHints.username;AutofillHints.password外还有很多,像email、Address、telephoneNumber、creditCard等等,大家可以进autofill.dart这个类里看看很多种类的填充服务。
      1. 如果说你是输入了新的用户名和密码,需要保存到KeyChain或者AutofillService中去的时候,就需要调用TextInput.finishAutofillContext(),他可以放到Sign In按钮功能登录成功后调用,另外其实它有个可选参数的static void finishAutofillContext({ bool shouldSave = true });默认shouldSave为true表示需要保存,如果不需要保存的时候则可以传shouldSave为false。
      void doLogin(BuildContext context) async {
        TextInput.finishAutofillContext();
        Navigator.push(context, MaterialPageRoute(
            builder: (context){
              return Container(
                color: Colors.redAccent,
              );
            }
          )
        );
      }
    
    3.2 如果说你点击输入框,弹出了用户名/密码选择器,你选了个密码,发现选错了删除后,再想从AutoFillService选择用户名/密码时,发现它弹不出来了。

    那么这时候TextInput.finishAutofillContext()就派上用场,我的做法是在空白处点击,当用户名或者密码有一个为空的时候调用TextInput.finishAutofillContext(shouldSave: false);当调用这句代码的时候代表此处AutofillService结束,输入框也会取消焦点,你再次点击输入框的时候又会再次弹出用户名/密码选择列表。

    Scaffold(
              body: Container(
                  child: GestureDetector(
                      behavior: HitTestBehavior.translucent,
                      onTap: () {
                        String password = passwordController.text;
                        String userName = userNameController.text;
                        if (StringFormatUtil.isStringEmpty(userName) ||
                            StringFormatUtil.isStringEmpty(password)) {
                          TextInput.finishAutofillContext(shouldSave: false);
                        }
                      },
                     ),
                ),
              ),
    
    3.3 上面所说的是,当两个输入框用户名和密码输入框都在一起的时候,当用户名和密码输入框在两个页面的时候咋办?😓

    我这里的情况是这样的:

    • 第一个页面输入用户名,并Check正确后在跳转到密码页
    • 将用户名带到密码页,并显示用户名,和密码输入框


      用户名页
    密码页
      1. 针对第一个用户名页面,需要填充UserName时,还是一样玩:需要AutofillGroup包裹TextField(如果没有包裹,点击输入框也可以弹出用户名选择器,但是点击了不能自动填充),autofillHints这个属性就是老生常谈了。
      Widget buildUserNameView(BuildContext context) {
        return AutofillGroup(
            child: TextField(
                  controller: usernameController,
                  decoration: InputDecoration(labelText: 'Username'),
                  keyboardType: TextInputType.name,
                  textInputAction: TextInputAction.next,
                  autofillHints: [AutofillHints.username],
                ),
        );
      }
    
      1. 在密码页,因为之前UserName是使用Text控件而不是TextField,如果需要实现Autofill保存功能,只能是两个TextField在上下一起,即:用户名输入框和密码输入框。
    密码页

    如果我们要实现这样的UI同时又满足都是TextField情况,那么只能吧UserName显示的Widget从Text改为TextField,而这个用户名的输入框,没有背景并且不能点击且autofillHints: [AutofillHints.username]。

    但是实际过程中发现,如果设置了TextField属性readOnly: true或者enabled: false和autofillHints: [AutofillHints.username],会发现报错。也就是当TextField 不可点击状态和autofillHints,不能同时存在,它们互斥。找到editable_text。我们看到了报错处:会断言有readOnly就不能有autofillHints;有autofillHints就不能readOnly。

          assert(
             !readOnly || autofillHints == null,
             "Read-only fields can't have autofill hints.",
           ),
    

    这里的话我们就只能魔改,使之成为本地Widget,将这个断言注释了。

    import 'package:base_widget/editable_text/text_field.dart' as MargicTextField;
    
    userNameController.value = TextEditingValue(text: userName);
    Widget buildPasswordTextFiledWidget() => AutofillGroup(
            child: Column(
              children: [
                //这里是魔改注释了上面断言的本地TextField
                MargicTextField.TextField(
                  controller: usernameController,
                  textInputAction: TextInputAction.next,
                  readOnly: true,
                  textAlign: TextAlign.center,
                  autofillHints: [AutofillHints.username],
                ),
                TextField(
                  controller: passwordController,
                  decoration: InputDecoration(labelText: 'Password'),
                  keyboardType: TextInputType.visiblePassword,
                  autofillHints: [AutofillHints.password],
                  onEditingComplete: () => TextInput.finishAutofillContext(),
                ),
              ],
            ),
          );
    

    Flutter端的用法就差不多这样了。

    四、Android端运行需要做的事儿

    如果在Flutter端已经配置后autofillHints和TextInput.finishAutofillContext()的话,就很简单了。Android手机上需要打开AutofillService服务,因为它默认是关闭的。

    路径: 设置 -> 系统 -> 语言与输入法 -> 高级 -> 输入帮助 -> 自动填充服务

    藏得比较深,有些手机路径可能不绝对这样,但也差不多。这里选择一个自动填充服务就好了。功能就可以使用了。

    自动填充服务选择

    五、iOS端的配置

    iOS就比较麻烦了:

      1. 首先得将设置 -> 密码 -> 自动填充密码 开关打开
      1. 然后需要配置Web Credentials,如果没有配置的话,能取到密码,但是不能保存。

    用户在Safari登录网站时,通常会在iCloud钥匙串中保存用户名和密码。随后,用户可能会打开源于同一个开发者的应用程序来访问同一个帐户。使用webcredentials,应用可以访问为网站存储的证书,无需用户重新输入用户名和密码。用户还可以在应用内创建新帐户,更新密码或删除帐户,Safari会保存并使用这些修改。

    具体如何配置看看这篇文章UniversalLinks和Web Credentials配置

    将Web Credentials相关文件(需命名为apple-app-site-association)写好,并由后端人员将apple-app-site-association文件上传到HTTPS Web服务器(该步骤需要将写好的apple-app-site-association文件交给服务端人员,让他们完成上传过程。)。放在服务器的根目录或.well-known子目录中。https://<domain>/apple-app-site-associationor https://<domain>/.well-known/apple-app-site-association。

    另外需要在工程下做如下配置:

    • xcode打开iOS工程配置Associated Domains


      配置好

    六、结语

    到这,KeyChain/Autofill自动填充功能就介绍完了,留个脚印,以备不时之需~

    相关文章

      网友评论

        本文标题:Flutter中实现Autofill(Android)&Keyc

        本文链接:https://www.haomeiwen.com/subject/rrsqtrtx.html