Flutter with Sqflite + Getx 数据更新问题

Flutter with Sqflite + Getx data update issues

我目前在Flutter中使用GetX和Sqflite,遇到插入后数据更新的问题。

首先,有一个 LoadingScreen 检查用户数据库中是否有任何条目。如果 none,则它会打开用户填写的注册页面。如果用户数据库中已有条目,则打开登录页面。

注册页面完成后,数据将写入用户数据库,然后用户将被带到主页,用户名显示在抽屉中。

用户在 LoadingScreenController 中使用 .obs,我在主页中使用 Obx 作为用户名。尽管如此,插入命令完成后的数据并没有自动更新。

但是,如果我在HomePage的init中再次调用fetch函数,那么新的数据就会被引入。

我的问题是,每次数据库更新时我都必须 运行 获取函数吗?这不违背 Getx 的目的吗?显然,我哪里错了。

下面是主页、LoadingScreen、LoadingScreenController、注册页面和数据库助手的代码。

在此方面如有任何帮助,我们将不胜感激。

主页代码:

class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  LoadingController loadingController = Get.find();

  var notifyHelper;
  @override
  void initState() {
    super.initState();
    notifyHelper = NotifyHelper();
    notifyHelper.initializeNotification();
    notifyHelper.requestIOSPermissions();
    username = loadingController.users[0].name.toString();
    usermail = loadingController.users[0].mail.toString();
  }

  String name = '', usernames = '', usermail = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _appbar(context),
      body: SingleChildScrollView(
        scrollDirection: Axis.vertical,
        child: Center(
          child: Obx(() => Text(loadingController.users.length.toString())),
        ),
      ),
      drawer: MyDrawer(usernames, usermail),
    );
  }

  _appbar(BuildContext context) {
    return AppBar(
      //leading: Drawer(),
      actions: [
        const Icon(
          Icons.person,
          size: 20.0,
        ),
      ],
    );
  }
}

加载屏幕

class LoadingScreen extends StatelessWidget {
  final loadingController = Get.put(LoadingController());
  final splashController = Get.put(SplashController());
  final TextEditingController passwordController = TextEditingController();
  int index = 0;

  LoadingScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //backgroundColor: Colors.black,
      body: SafeArea(
        child: SingleChildScrollView(
          child: Column(
            children: [
              const SizedBox(
                height: 20,
              ),
              _splash(context),
              GetBuilder<SplashController>(
                  builder: (_) => splashController.completes.value == false
                      ? MyTextContainer(
                          20.0,
                          'Counting Your Money ... \n Please wait',
                          .4,
                          true,
                          subTitleStyle)
                      : const Text('')),
              GetBuilder<LoadingController>(
                builder: (_) => loadingController.users.length > 0
                    ? _signin(
                        context, loadingController.users[0].name.toString())
                    : _signup(context),
              ),
            ],
          ),
        ),
      ),
    );
  }

  onTap() {
    if (passwordController.text !=
        loadingController.users[0].password.toString()) {
      Get.snackbar('Alert', 'Passwords do not match',
          snackPosition: SnackPosition.BOTTOM);
    } else {
      Get.to(() => const Home());
    }
  }

  Widget _splash(BuildContext context) {
    return Center(
        child: GetBuilder<SplashController>(
      builder: (_) => ScaleTransition(
        scale: splashController.anim,
        child: Container(
            margin: const EdgeInsets.only(left: 8.0, top: 8.0, right: 8.0),
            padding: const EdgeInsets.only(bottom: 12.0),
            decoration: BoxDecoration(
              gradient: const LinearGradient(
                begin: Alignment.centerLeft,
                end: Alignment.centerRight,
                colors: [primaryClr, yellowClr],
              ),
              borderRadius: BorderRadius.circular(18),
              border: Border.all(color: Colors.white, width: 2.0),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Lottie.asset('assets/loadingScreen.json',
                    height: MediaQuery.of(context).size.height * .40),
                Text('Welcome ', style: titleStyle),
              ],
            )),
      ),
    ));
  }

  Widget _signin(BuildContext context, String username) {
    return GetBuilder<SplashController>(
      builder: (_) => ScaleTransition(
        scale: splashController.anim,
        child: Container(
          margin: const EdgeInsets.only(left: 10.0, right: 10.0),
          child: Column(
            children: [
              Text(
                "Welome back $username \nEnter Password to Sign-In",
                textAlign: TextAlign.center,
              ),
              MyTextInputField(
                title: 'Password',
                hint: 'Enter Your Password',
                textStyle: subTitleStyle,
                textInputType: TextInputType.number,
                textCapital: TextCapitalization.none,
                controller: passwordController,
                isObscure: true,
              ),
              const SizedBox(
                height: 20.0,
              ),
              passwordController.text.isEmpty
                  ? Container()
                  : MyButton(
                      label: 'SignIn',
                      onTap: onTap,
                      textStyle: titleStyle,
                      colorScheme: primaryClr,
                    ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _signup(BuildContext context) {
    return GetBuilder<SplashController>(
      builder: (_) => ScaleTransition(
        scale: splashController.anim,
        child: Column(
          children: [
            Text(
              "This is your first Login \nClick here to Sign-Up",
              style: subTitleStyle,
            ),
            const SizedBox(
              height: 20.0,
            ),
            MyButton(
              label: 'Sign-Up',
              textStyle: titleStyle,
              onTap: () => Get.to(() => const SignUp()),
              colorScheme: primaryClr,
            ),
            
          ],
        ),
      ),
    );
  }
}

加载屏幕控制器

class LoadingController extends GetxController {
  // ignore: deprecated_member_use
  //var allacc = List<AllAccounts>().obs;

  var users = <User>[].obs;
  var allaccounts = <AllAccounts>[].obs;
  List<AllAccounts> allacc = <AllAccounts>[].obs;

  @override
  void onInit() async {
    await fetchUsers();
    await fetchAllAccounts();
    super.onInit();
  }

  fetchUsers() async {
    var userList = await DatabaseHelper.db.getAllUsers();
    users.assignAll(userList);
    update();
  }

  fetchAllAccounts() async {
    await DatabaseHelper.db.getAllAccounts().then((accountList) {
      allaccounts.value = accountList.cast<AllAccounts>();
      update();
    });
  }

  Future<void> insertUser({User? user}) async {
    await DatabaseHelper.db.insertUser(user!);
    update();
  }
}

DatabaseHelper(没有数据库创建代码)

Future<int> insertUser(User user) async {
    Database dbase = await database;
    var res = dbase.insert(User.tblUser, user.toMap());
    return res;
  }

  Future<List<User>> getAllUsers() async {
    Database db = await database;
    List<Map<String, dynamic>> username = await db.rawQuery('''
        SELECT COALESCE(${User.colUserId},'') as 'id', COALESCE(${User.colUserName},'') as 'name', 
        COALESCE(${User.colUserPhone},'') as 'phone',  COALESCE(${User.colUserMail},'9999') as 'mail',
        COALESCE(${User.colUserPassword},'') as 'password', COALESCE(${User.colUserStartDate},'') as 'startdate'
        FROM ${User.tblUser}
    ''');
    //print('Printing from db $usernames');
    return username.isEmpty
        ? []
        : username.map((e) => User.fromMap(e)).toList();
  }

注册

class SignUp extends StatefulWidget {
  const SignUp({Key? key}) : super(key: key);

  @override
  State<SignUp> createState() => _SignUpState();
}

class _SignUpState extends State<SignUp> {
  final TextEditingController nameController = TextEditingController();
  final TextEditingController phoneController = TextEditingController();
  final TextEditingController mailController = TextEditingController();
  final TextEditingController passwordController = TextEditingController();
  final TextEditingController repasswordController = TextEditingController();
  final TextEditingController startdateController = TextEditingController();
  final LoadingController loadingController = Get.find();

  String alertmsg = '';

  Future<void> onTap() async {
    if (_validateText() == null) {
      _showSnackBar('Writing Data... Please wait');
      _addDataToDB();
    }

    //Get.to(() => Home());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: _appbar(context),
        body: SafeArea(
          child: Container(
            padding: const EdgeInsets.only(left: 20, top: 10, right: 20),
            child: SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Sign-Up', style: headingStyle),
                  MyTextInputField(
                    title: 'Name',
                    hint: 'Enter First Name and Last Name here',
                    textStyle: subTitleStyle,
                    textInputType: TextInputType.text,
                    textCapital: TextCapitalization.words,
                    controller: nameController,
                  ),
                  MyTextInputField(
                    title: 'Phone Number',
                    hint: 'Phone Number is to send reminders - Required',
                    textStyle: subTitleStyle,
                    textInputType: TextInputType.number,
                    textCapital: TextCapitalization.none,
                    controller: phoneController,
                  ),
                  MyTextInputField(
                    title: 'Mail ID',
                    hint: 'Mail ID is to back up the Data - Required',
                    textStyle: subTitleStyle,
                    textInputType: TextInputType.emailAddress,
                    textCapital: TextCapitalization.none,
                    controller: mailController,
                  ),
                  MyTextInputField(
                    title: 'Password',
                    hint: 'Enter Pasword Here - Minimum of 4 digits',
                    textStyle: subTitleStyle,
                    textInputType: TextInputType.number,
                    textCapital: TextCapitalization.none,
                    controller: passwordController,
                    isObscure: true,
                  ),
                  MyTextInputField(
                    title: 'Confirm Password',
                    hint: 'Re-enter Your Pasword Here',
                    textStyle: subTitleStyle,
                    textInputType: TextInputType.number,
                    textCapital: TextCapitalization.none,
                    controller: repasswordController,
                    isObscure: true,
                  ),
                  MyTextInputField(
                      title: 'Start Date',
                      hint: 'Date from when the Transactions start',
                      textStyle: subTitleStyle,
                      textInputType: TextInputType.number,
                      textCapital: TextCapitalization.none,
                      controller: startdateController,
                      widget: IconButton(
                        icon: const Icon(Icons.calendar_today_outlined),
                        onPressed: () {
                          print('Printed');
                        },
                      )),
                  const SizedBox(
                    height: 20.0,
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: [
                      MyButton(
                        label: 'Confirm',
                        onTap: onTap,
                        textStyle: titleStyle,
                        colorScheme: primaryClr,
                      )
                    ],
                  )
                ],
              ),
            ),
          ),
        ));
  }

  _addDataToDB() async {
    await loadingController
        .insertUser(
            user: User(
                name: nameController.text,
                phone: phoneController.text,
                mail: mailController.text,
                password: passwordController.text,
                startdate: DateTime.now().toString(),
                created: DateTime.now().toString()))
        .then((value) {
      _showSnackBar('Data Saved');
      Get.to(() => Home());
    });
  }

  _validateText() {
    if (nameController.text.isEmpty) {
      alertmsg = 'Name is Required';
      _showSnackBar(alertmsg);
      return alertmsg;
    } else if (_validatePhone(phoneController.text) != null) {
      _showSnackBar(alertmsg);
      return alertmsg;
    } else if (_validateMail(mailController.text) != null) {
      _showSnackBar(alertmsg);
      return alertmsg;
    } else if (_validatePassword(
            passwordController.text, repasswordController.text) !=
        null) {
      _showSnackBar(alertmsg);
      return alertmsg;
    }
    return null;
  }

  String? _validatePhone(String value) {
    if (value.isEmpty || value.length < 10) {
      alertmsg = 'Please enter valid Phone Number';
      // _showSnackBar(alertmsg);
      return alertmsg;
    }
    return null;
  }

  String? _validateMail(String value) {
    bool emailValid = RegExp(
            r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
        .hasMatch(value);
    if (!emailValid) {
      alertmsg = 'Please enter valid mail ID';

      return alertmsg;
    }
    return null;
  }

  String? _validatePassword(String value1, String value2) {
    print('$value1 $value2');
    if (!value1.isNum && value1.length < 4) {
      alertmsg = 'Password should be atleast a 4 digit Number ';
      _showSnackBar(alertmsg);
      return alertmsg;
    } else if (value2.isEmpty) {
      alertmsg = 'Confirm Password';
      _showSnackBar(alertmsg);
      return alertmsg;
    } else if (value1 != value2) {
      alertmsg = 'Passwords do not match';
      _showSnackBar(alertmsg);
      return alertmsg;
    }
  }

  _showSnackBar(String value) {
    Get.snackbar('Alert', value, snackPosition: SnackPosition.BOTTOM);
  }

  _appbar(BuildContext context) {
    return AppBar(
      backgroundColor: Theme.of(context).colorScheme.primary,
      leading: InkWell(
        onTap: () {
          Get.back();
        },
        child: const Icon(Icons.arrow_back),
      ),
    );
  }
}

您必须在每次 add/update/delete 操作后调用获取数据,除非您的数据库以某种方式 returns 一个流。它并没有违背 GetX 的宗旨。这是预期的行为。用户应该在代码中明确定义他们想要做什么。

另一件事:您在使用 .obs 时正在使用 GetBuilder。这是行不通的,因为 GetBuilder 不是反应性的,并且当可观察对象 (.obs) 发生变化时不会 react/update UI。您应该改用 GetXObx