键盘打开/关闭后颤振重建

时间:2021-03-15 21:01:30

标签: android flutter dart

目前,我正在处理应用程序的更新配置文件部分,但似乎存在问题。问题是当我们想要进入页面时,我们想要查看加载了先前数据或旧数据使用情况的文本字段。为此,我们使用未来的构建器来获取数据,然后将其设置到字段中。但是,当我们使用移动设备上的键盘点击和编辑文本字段时,未来的构建器会再次重建,因为屏幕尺寸的这种变化会调用构建,键盘会上下移动。在我们修复他时,我必须做的是创建一个临时的未来变量,并且只有在它为空时才会更新。最重要的是,文本字段仅在计数等于某个值 IE 时才会更新,在这种情况下,为 1 但它不起作用。我们希望每次进入页面 AKA 刷新或单击提交按钮时在文本字段中放置文本。这就是我遇到麻烦的地方。我曾尝试使用状态,但得到了相同的结果,可能是我设置错误。

目标:加载数据,使用我刚获得的数据设置文本字段,根据需要编辑它们(数据保持原样,键盘向上或向下)。然后通过更新按钮更新该数据。


old:
class Test extends StatelessWidget {

  Future<void> getFuture() async {
    _profile = await HTTP.getProfile();
  }
  
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: getFuture(); // generate every time
        builder: () {
           return nameFeild();
        }
     )
  }
  
  Widget nameFeild(){
        _changeNameController.text = _profile.name;
        return TextField(controller: _nameController);
    }
}
new:
class Test extends StatelessWidget {
  Future testFuture;
  static int count = 0;
  Future<void> getFuture() async {
    _profile = await HTTP.getProfile();
  }
  
  Widget build(BuildContext context) {
    testFuture ??= getFuture();
    count++;
    return FutureBuilder(
        future: testFuture; // generate every time
        builder: () {
           return nameFeild();
        }
     )
  }
  
  Widget nameFeild(){
    if(count == 1){
      _changeNameController.text = _profile.name;
    }    
    return TextField(controller: _nameController);
    }
}

实际代码

// imports

class UserSettingPage extends StatelessWidget {
  BuildContext context;
  GetSizes _sizes;
  double _w;
  double _h;
  NavbarWidget _navBar = NavbarWidget();
  FooterWidget _footer = FooterWidget.autoPosition();
  ImageProvider _userPicture =
      AssetImage('assets/images/team/zain.png'); //Defaultpic.png');
  Cookies _cookies = Cookies();

  final _oldPasswordControllerOne = TextEditingController();
  final _resetPasswordControllerOne = TextEditingController();
  final _resetPasswordControllerTwo = TextEditingController();

  final _changeNameController = TextEditingController();
  final _changeBioController = TextEditingController();
  final _changeNumberController = TextEditingController();

  final _changeFacebookController = TextEditingController();
  final _changeTwitterController = TextEditingController();
  final _changeInstagramController = TextEditingController();

  CheckBoxValueNotifier email = new CheckBoxValueNotifier(false);
  CheckBoxValueNotifier phone = new CheckBoxValueNotifier(false);

  bool _isDesktop;
  Authentication _authentication = Authentication();
  Encryption _encryption = Encryption();
  DialogBox _dialog = DialogBox();
  passwordValidNotifier passwordNotifier = new passwordValidNotifier();
  ProfileModel _user;
  Uint8List _base64;

  bool phoneCheckedValue = false;
  bool emailCheckedValue = false;
  String userID;
  FilePickerWidget imagePicker = FilePickerWidget();
  Future _getUser;
  static int count = 0;

  Future<String> retrieveUser() async {
    userID = await Cookies.getCookieValue("userID");
    await DioCalls.getProfile(context, userID).then((value) => _user = value);
  }

  @override
  Widget build(BuildContext context) {
    this.context = context;
    _sizes = GetSizes(context);
    _w = _sizes.getPageWidth();
    _h = _sizes.getPageHeight();
    _isDesktop = _sizes.isDesktop();
    print('Update Data: ' + (_getUser == null).toString() + ' count: ' + count.toString());
    _getUser ??= retrieveUser();
    count++;
    return LayoutBuilder(builder: (context, constraints) {
      if (_sizes.isDesktop()) {
        return _bigDisplay();
      } else {
        return _smallDisplay();
      }
    });
  }

  Widget _bigDisplay() {
    // code
  }

  Widget _smallDisplay() {
    return FutureBuilder(
      future: _getUser,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(child: Text(''));
        } else {
          if (snapshot.hasError) {
            return Center(child: Text('Errors: ${snapshot.error}'));
          } else {
            return Scaffold(
              backgroundColor: Colors.white,
              body: Column(
                children: [
                  _navBar,
                  Expanded(
                    child: SingleChildScrollView(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [userPictureAndBorder()],
                          ),
                          userInfo(),
                          line(),
                          changeNameTile(),
                          line(),
                          changeBioTile(),
                          line(),
                          changeSocialMediaTile(),
                          line(),
                          changeNotificationTile(),
                          line(),
                          changePasswordTile(),
                          line(),
                          Container(
                            margin: EdgeInsets.fromLTRB(0, 10, 0, 10),
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                              children: [logoutButton(), updateButton(context)],
                            ),
                          )
                        ],
                      ),
                    ),
                  )
                ],
              ),
            );
          }
        }
      },
    );
  }
  
// OTHER WIDGETS CODE WERE REMOVED, I dont think you need them
 
  Widget userInfo() {
    return Container(
      margin: EdgeInsets.fromLTRB(0, 5, 0, 10),
      alignment: Alignment.center,
      child: Column(
        children: [
          SelectableText(
            _user.userID,
            style: TextStyle(
              fontWeight: FontWeight.w800,
              fontSize: 14,
              color: Colors.black,
              // height: 21,
            ),
          ),
          SelectableText(
            _user.name,
            style: TextStyle(
              fontWeight: FontWeight.w400,
              fontSize: 14,
              color: Colors.black,
              // height: 21,
            ),
          ),
          SelectableText(
            '732-318-5555 (change)',
            style: TextStyle(
              fontWeight: FontWeight.w400,
              fontSize: 14,
              color: Colors.black,
              // height: 21,
            ),
          ),
        ],
      ),
    );
  }


  Widget logoutButton() {
    return ElevatedButton.icon(
      onPressed: () {
        print('Logging out');
      },
      icon: Icon(Icons.logout),
      label: Text("Logout"),
      style: ElevatedButton.styleFrom(
        primary: Colors.black, // background
        onPrimary: Colors.white, // foreground
      ),
    );
  }

  Widget updateButton(BuildContext context) {
    return ElevatedButton.icon(
      onPressed: () async {
        _user.name = _changeNameController.text;
        _user.bio = _changeBioController.text;
        _user.socialMedia.Facebook = _changeFacebookController.text;
        _user.socialMedia.Instagram = _changeInstagramController.text;
        _user.socialMedia.Twitter = _changeTwitterController.text;

        DioCalls.updateProfile(context, _user, imagePicker.selectedFile,
            await Cookies.getCookieValue("jwt"));
        Navigator.pop(context); // pop current page
        Navigator.pushNamed(context, "/test1"); // push it back in
        _oldPasswordControllerOne.clear();
        _changeBioController.clear();
        count = 0;
        _getUser = null;
      },
      icon: Icon(Icons.update),
      label: Text("Update"),
      style: ElevatedButton.styleFrom(
        primary: Colors.black, // background
        onPrimary: Colors.white, // foreground
      ),
    );
  }

  Widget nameTextField() {
    return SelectableText(
      "Edit Name",
      textAlign: TextAlign.start,
      style: TextStyle(
        fontSize: 16,
        fontStyle: FontStyle.normal,
        fontFamily: 'Poppins-Black',
        fontWeight: FontWeight.w500,
      ),
    );
  }

  Widget bioTextField() {
    return SelectableText(
      "Edit Bio (Max: 100)",
      textAlign: TextAlign.start,
      style: TextStyle(
        fontSize: 16,
        fontStyle: FontStyle.normal,
        fontFamily: 'Poppins-Black',
        fontWeight: FontWeight.w500,
      ),
    );
  }

  Widget changeNameTile() {
    if (count == 1) {
      print('updating text');
      _changeNameController.text = _user.name;
    }
    return ExpansionTile(
      title: Text(
        'Name',
        style: TextStyle(
            fontSize: (_sizes.isDesktop()) ? 16 : 12,
            fontWeight: FontWeight.w500,
            color: Colors.black),
        textAlign: TextAlign.left,
      ),
      children: [
        nameTextField(),
        Container(
          margin: EdgeInsets.all(20),
          child: Theme(
            data: ThemeData(
                primaryColor: Colors.transparent,
                hintColor: Colors.transparent),
            child: TextField(
              maxLength: 50,
              controller: _changeNameController,
              decoration: InputDecoration(
                counterText: '',
                contentPadding: EdgeInsets.all(20.0),
                enabledBorder: OutlineInputBorder(
                  borderSide: BorderSide(),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
                errorBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Theme.of(context).errorColor),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
                focusedBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Theme.of(context).primaryColor),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
                border: OutlineInputBorder(
                  borderSide: BorderSide(),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
              ),
            ),
          ),
        )
      ],
    );
  }

  Widget changeBioTile() {
    if (count == 1) {
      _changeBioController.text = _user.bio;
    }

    return ExpansionTile(
      title: Text(
        'Bio',
        style: TextStyle(
            fontSize: (_sizes.isDesktop()) ? 16 : 12,
            fontWeight: FontWeight.w500,
            color: Colors.black),
        textAlign: TextAlign.left,
      ),
      children: [
        bioTextField(),
        Container(
          margin: EdgeInsets.all(20),
          child: Theme(
            data: ThemeData(
                primaryColor: Colors.transparent,
                hintColor: Colors.transparent),
            child: TextField(
              maxLength: 100,
              controller: _changeBioController,
              decoration: InputDecoration(
                counterText: '',
                contentPadding: EdgeInsets.all(20.0),
                enabledBorder: OutlineInputBorder(
                  borderSide: BorderSide(),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
                errorBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Theme.of(context).errorColor),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
                focusedBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Theme.of(context).primaryColor),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
                border: OutlineInputBorder(
                  borderSide: BorderSide(),
                  borderRadius: BorderRadius.all(Radius.circular(
                          10) //                 <--- border radius here
                      ),
                ),
              ),
            ),
          ),
        )
      ],
    );
  }

  Widget changeSocialMediaTile() {
    if (count == 1) {
      _changeFacebookController.text = _user.socialMedia.Facebook;
      _changeTwitterController.text = _user.socialMedia.Twitter;
      _changeInstagramController.text = _user.socialMedia.Instagram;
    }

    return ExpansionTile(
      title: Text(
        'Social Media',
        style: TextStyle(
            fontSize: (_sizes.isDesktop()) ? 16 : 12,
            fontWeight: FontWeight.w500,
            color: Colors.black),
        textAlign: TextAlign.left,
      ),
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(
              MyFlutterApp.facebook,
              color: Colors.blue,
              size: 30,
            ),
            SelectableText("www.Facebook.com/"),
            Container(
              margin: EdgeInsets.all(5),
              width: 200,
              child: Theme(
                data: ThemeData(
                    primaryColor: Colors.transparent,
                    hintColor: Colors.transparent),
                child: TextField(
                  maxLength: 100,
                  controller: _changeFacebookController,
                  decoration: InputDecoration(
                    counterText: '',
                    contentPadding: EdgeInsets.all(5.0),
                    enabledBorder: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    errorBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).errorColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).primaryColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    border: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                  ),
                ),
              ),
            )
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(
              MyFlutterApp.twitter,
              color: Colors.lightBlue,
              size: 30,
            ),
            SelectableText("www.Twitter.com/     "),
            Container(
              margin: EdgeInsets.all(5),
              width: 200,
              child: Theme(
                data: ThemeData(
                    primaryColor: Colors.transparent,
                    hintColor: Colors.transparent),
                child: TextField(
                  maxLength: 100,
                  controller: _changeTwitterController,
                  decoration: InputDecoration(
                    counterText: '',
                    contentPadding: EdgeInsets.all(5.0),
                    enabledBorder: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    errorBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).errorColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).primaryColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    border: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                  ),
                ),
              ),
            )
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(MyFlutterApp.instagram, color: Colors.black, size: 30.0),
            SelectableText("www.Instagram.com/"),
            Container(
              margin: EdgeInsets.all(5),
              width: 200,
              child: Theme(
                data: ThemeData(
                    primaryColor: Colors.transparent,
                    hintColor: Colors.transparent),
                child: TextField(
                  maxLength: 100,
                  controller: _changeInstagramController,
                  decoration: InputDecoration(
                    counterText: '',
                    contentPadding: EdgeInsets.all(5.0),
                    enabledBorder: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    errorBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).errorColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).primaryColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    border: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                  ),
                ),
              ),
            )
          ],
        )
      ],
    );
  }

  Widget changeNotificationTile() {
    return ExpansionTile(
      title: Text(
        'Notifications',
        style: TextStyle(
            fontSize: (_sizes.isDesktop()) ? 16 : 12,
            fontWeight: FontWeight.w500,
            color: Colors.black),
        textAlign: TextAlign.left,
      ),
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.phone_iphone),
            Container(
              margin: EdgeInsets.all(5),
              width: 200,
              child: Theme(
                data: ThemeData(
                    primaryColor: Colors.transparent,
                    hintColor: Colors.transparent),
                child: TextField(
                  maxLength: 100,
                  controller: _changeNumberController,
                  inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
                  ],
                  decoration: InputDecoration(
                    counterText: '',
                    contentPadding: EdgeInsets.all(5.0),
                    enabledBorder: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    errorBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).errorColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderSide:
                          BorderSide(color: Theme.of(context).primaryColor),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                    border: OutlineInputBorder(
                      borderSide: BorderSide(),
                      borderRadius: BorderRadius.all(Radius.circular(
                              10) //                 <--- border radius here
                          ),
                    ),
                  ),
                ),
              ),
            )
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              width: 275,
              child: emailTile(
                checkBoxValueNotifier: email,
              ),
            )
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              width: 275,
              child: phoneTile(
                checkBoxValueNotifier: phone,
              ),
            )
          ],
        )
      ],
    );
  }
}

class emailTile extends StatefulWidget {
  @override
  final CheckBoxValueNotifier checkBoxValueNotifier;

  const emailTile({Key key, this.checkBoxValueNotifier}) : super(key: key);

  _emailTileState createState() => _emailTileState();
}

class _emailTileState extends State<emailTile> {
  @override
  Widget build(BuildContext context) {
    return CheckboxListTile(
      value: widget.checkBoxValueNotifier.check.value,
      secondary: Icon(
        Icons.email_rounded,
        color: Colors.black,
        size: 30,
      ),
      title: SelectableText("Email Notifications"),
      onChanged: (newValue) {
        setState(() {
          widget.checkBoxValueNotifier.check.value =
              !widget.checkBoxValueNotifier.check.value;
        });
      },
      controlAffinity:
          ListTileControlAffinity.trailing, //  <-- leading Checkbox
    );
  }
}

class phoneTile extends StatefulWidget {
  @override
  final CheckBoxValueNotifier checkBoxValueNotifier;

  const phoneTile({Key key, this.checkBoxValueNotifier}) : super(key: key);

  _phoneTileState createState() => _phoneTileState();
}

class _phoneTileState extends State<phoneTile> {
  @override
  Widget build(BuildContext context) {
    return CheckboxListTile(
      value: widget.checkBoxValueNotifier.check.value,
      secondary: Icon(
        Icons.phone_iphone,
        color: Colors.black,
        size: 30,
      ),
      title: SelectableText("Phone Notifications"),
      onChanged: (newValue) {
        setState(() {
          widget.checkBoxValueNotifier.check.value =
              !widget.checkBoxValueNotifier.check.value;
        });
      },
      controlAffinity:
          ListTileControlAffinity.trailing, //  <-- leading Checkbox
    );
  }
}

class CheckBoxValueNotifier {
  ValueNotifier check;

  CheckBoxValueNotifier(bool init) {
    check = ValueNotifier(init);
  }

  void toggleNotifier() {
    check.value = !check.value;
  }
}

2 个答案:

答案 0 :(得分:0)

您是否尝试过查看如何处理运行时配置更改 here?使用来自 this 部分的可能解决方案的片段:

<activity android:name=".MyActivity"
          android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
          android:label="@string/app_name">

答案 1 :(得分:0)

old:new: 示例中,对 getFuture() 的调用都是在 build() 方法内完成的。

这不是任何类型的长时间通话的正确位置。 build() 通常应该只包含 UI 显示代码,因为小部件的 build 方法理论上每秒可以发生 60 次(假设您在小部件中使用动画)。

我猜每次您的小部件重建(由于键盘显示或其他需要重建的更改),您未来的数据调用都会再次发生,这会触发您的 FutureBuilder 的重建。

我建议从 Stateless 小部件切换到 StatefulWidget 并将 getFuture() 调用放入 initState()StatefulWidget 方法中仅在 StatefulWidget 实例化时运行一次,但不会在其重建时再次运行(这可能在您的键盘显示/隐藏等时发生)。

以下是一些说明如何工作的示例代码:

*(顺便说一句,当显示/隐藏键盘时,我没有看到以下代码的 StatefulStateless 版本的重建。不知道为什么。)

import 'package:flutter/material.dart';

class StatefulRebuildPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Base Stateless Page widget re/built');
    return Scaffold(
      appBar: AppBar(
        title: Text('Stateful Rebuild Page'),
      ),
      body: FormPageStateful(), // ← swap between these two to test
      //body: FormPageStateless(), // ← swap between these two to test
    );
  }
}

/// StateLESS version of FormPage
/// ////////////////////////////////////
class FormPageStateless extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('FormPageStateless re/built');
    return MyFutureBuilderWidget(FakeFutureDataSource.getData());
  }
}

/// StateFUL version of FormPage
/// ////////////////////////////////////
class FormPageStateful extends StatefulWidget {
  @override
  _FormPageStatefulState createState() => _FormPageStatefulState();
}

class _FormPageStatefulState extends State<FormPageStateful> {
  Future<String> _nameData = Future.value('loading data...');

  @override
  void initState() {
    super.initState();
    _nameData = FakeFutureDataSource.getData();
    /// Long data call happens ↑ here ↑ only once
  }

  @override
  Widget build(BuildContext context) {
    print('FormPage re/built');
    return MyFutureBuilderWidget(_nameData); // ← supply the future
  }
}

class MyFutureBuilderWidget extends StatelessWidget {
  final Future<String> _nameData;

  MyFutureBuilderWidget(this._nameData);

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      padding: EdgeInsets.symmetric(horizontal: 25),
      child: FutureBuilder<String>(
        future: _nameData, // ← when future returns, ↓ will get rebuilt with future data
        builder: (context, snapshot) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              _nameField(snapshot.hasData ? snapshot.data : 'loading...')
            ],
          );
        },
      ),
    );
  }

  Widget _nameField(String name) {
    return TextFormField( // ← is a stateful widget underneath
      key: ValueKey(name), // ← key used for framework to know this widget has changed & needs rebuilding
      decoration: InputDecoration(
        labelText: 'Name',
      ),
      initialValue: name,
    );
  }
}

class FakeFutureDataSource {
  static Future<String> getData() async {
    return await Future.delayed(Duration(seconds: 2), () {
      print('_fakeGetFuture() complete. Returning data...');
      return 'Billy';
    });
  }
}