屌炸天!国外同行这样用Chat GPT提高Flutter开发的效率!

16,292 阅读15分钟

本文正在参加「金石计划」

原文链接:medium.com/flutter-com…,翻译时根据需要做了删减。

前言

作为一个Flutter的开发者,我们肯定希望能够寻求手段来改进开发效率,让开发工作更顺畅。现在,Chat GPT能够帮我们达到这个目的了。本篇,我们将向你展示如何使用Chat GPT提高Flutter开发者的效率。

Chat GPT可以在哪些方面帮助Flutter开发者?

这是我们整理的Chat GPT最适合帮助Flutter 开发者的7个点:

  1. 生成代码

可以利用Chat GPT自动生成你需要的代码来提高效率。Chat GPT可以基于你已有的代码库进行训练来生成特定的代码片段。例如,如果你需要创建一个新的widget,你可以给Chat GPT提供对应的要求,然后它会帮你生成需要的代码。这可以帮你节省很多实现不同原型的时间。

  1. 测试和调试

Chat GPT可以测试和调试你的代码。可以在Chat GPT中输入特定的测试用例,然后它会生成对应的输出结果。这可以让你快速定位和修复代码的bug。此外,Chat GPT还可以生成一些你想不到的边界测试用例,使得你的代码更加健壮可靠。

  1. 给代码加注释

Chat GPT可以给Flutter代码增加注释,使得开发者可以更容易理解和解释复杂的代码结构和概念。

  1. 解释代码

Chat GPT可以为Flutter或其他程序代码生成解释说明。通过给Chat GPT提供一些描述代码的自然语义语料,模型可以给非技术人员生成可理解的代码说明。当然,效果的前提取决于提供的语料和提示语,以及代码自身的复杂程度。

  1. 生成文档

文档对于软件开发来说非常重要,但是编写文档非常耗时。通过Chat GPT,可以为你的代码库生成文档。例如,你可以给Chat GPT提供一个特定的函数或类,然后它会给出这个函数的用途说明以及如何使用这个函数,这将节省你的时间并且让你的代码库有比较好的文档说明。

  1. 给界面开发者提供示例文本

作为Flutter开发者,你经常需要给App创建UI界面。通过Chat GPT,可以给UI界面生成所需要的示例文本。这可以节省你的时间并且帮助你快速完成UI界面。例如,你设计一个即时聊天App,就可以用Chat GPT生成示例的聊天对话信息。

  1. 创建聊天机器人

你可能需要给你的应用增加一个聊天机器人,有了Chat GPT,你就可以训练他像人那样响应用户的输入,这可以提高用户体验和自动完成一些客服工作。

Chat GPT是代码生成的最佳工具吗

需要说明的是,Chat GPT是基于大量自然语言文本进行训练的,主要是应对自然语言的输入。虽然它可以产生Flutter或其他程序的代码及解释,但是它并不是专门设计做代码生成任务的。专门用于代码生成的工具有GPT-Code,Codex以及Open AI的DALL-E。因此,我们需要了解,Chat GPT生成的代码无法保障高质量,也不会考虑性能优化。此外,对AI模型生成的代码进行完备的测试和审查十分重要,这样才能够保障满足需求和没有安全隐患。

应用实例

现在我们来让Chat GPT帮我们干点Flutter的活,我们将通过6个场景来看看Chat GPT有多“能干”。

  1. 编写一个简单的数学函数

Flutter 开发者:你能使用Dart写一个函数吗?这个函数接收一个整数参数,然后返回一个介于0和这个整数之间的随机整数。

Chat GPT:没问题!我可以利用dart:math库编写一个函数来产生一个0到给定整数的随机整数。下面是示例,这个函数接收一个名称为n的整数参数,然后返回一个0到n-1的随机整数。

import 'dart:math';

int generateRandomNumber(int n) {
	Random random = Random();
	return random.nextInt(n);
}

Chat GPT:下面是这个函数的使用示例:

void main() {
  print(generateRandomNumber(10)); // Example output: 6
  print(generateRandomNumber(100)); // Example output: 56
  print(generateRandomNumber(5)); // Example output: 2
}

Chat GPT: 注意Random.nextInt() 方法返回的值是从0(含0)到给定的值(不包含),因此我们不需要像Python那样把n-1。

  1. 编写一个从firestrore读取数据的函数

Flutter 开发者:你能编写一个从firestore中读取document中国家字段为“UK”的所有销量数据的Flutter函数吗。在每个document记录中有一个字段叫做“amountSold”,请计算所有国家字段为“UK”的总的“amountSold”,然后返回这个计算结果。

Chat GPT:好的。我可以完成这个函数的编写。下面是使用cloud_firestore插件实现与FireStore交互的示例函数:

import 'package:cloud_firestore/cloud_firestore.dart';

Future<double> calculateTotalSalesInUK() async {
  double totalSalesInUK = 0;
  QuerySnapshot querySnapshot = await FirebaseFirestore.instance
      .collection('sales')
      .where('country', isEqualTo: 'UK')
      .get();
  querySnapshot.docs.forEach((document) {
    totalSalesInUK += document.data()['amountSold'];
  });
  return totalSalesInUK;
}

Chat GPT: 下面是在Flutter App里使用这个方法的示例:

double ukSales = await calculateTotalSalesInUK();
print('Total sales in UK: $ukSales');

Chat GPT: 注意这是个异步方法,因此在获得总的销售数量前需要等待。此外,querySnapshot.docs属性是所有满足查询条件的文档列表,这里使用了forEach方法对每个文档进行迭代来计算总的销量数值。最终,我们将销量作为double类型返回。

  1. 编写一个firebase云原生函数

Flutter开发者: 早上好! 请编写一个名称为“userRegisration”的firebase云原生函数。这个函数在firestore中的“customers”集合新增一条文档时触发。每个文档都有一个对应用户电子邮箱的“email”字段,以及一个“name”字段和一个“phone”字段。一旦这个文档创建的时候,这个函数就会使用用户的电子邮箱在“firebase auth”里注册一个账户。请设置一个随机的密码。然后,这个函数必须复制firestore的这个文档作为用户集合中的新文档。请将这个文档的名称命名为firebase的用户id(uid)。最后,请利用原文档的信息来填充刚刚创建的用户的属性。例如设置授权的“displayName”作为用户的“name”字段值,以及设置用户的电话作为字段“phone”的值。

Chat GPT: 没问题,下面是你需要的云原生函数:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp();

const firestore = admin.firestore();
const auth = admin.auth();

exports.userRegistration = functions.firestore
    .document('customers/{customerId}')
    .onCreate(async (snap, context) => {
        const data = snap.data();
        const email = data.email;
        const name = data.name;
        const phone = data.phone;

        // Create a random password for the new user
        const password = Math.random().toString(36).slice(-8);

        // Create a new Firebase Auth user with the provided email and password
        const user = await auth.createUser({
            email: email,
            password: password
        });

        // Set the Firebase Auth display name to the name field in the document
        await auth.updateUser(user.uid, {
            displayName: name
        });

        // Create a new document in the users collection with the same data as the original document
        await firestore.collection('users').doc(user.uid).set({
            name: name,
            phone: phone
        });

        console.log(`New user with email ${email} and UID ${user.uid} has been created.`);

        return null;
    });

Chat GPT:这个函数监听Firestore中客户集合的创建行为。一旦一个新的客户文档创建了,它会从文档中获取电子邮箱、姓名和电话等数据。然后,它为新的用户生成了一个随机密码,并使用电子邮箱和密码创建了一个新的Firebase Auth用户。 接下来,它将Firebase Auth的“displayName”设置为用户的“name”字段的值,之后使用了原文档相同的姓名和电话字段在用户集合中创建了一个新的文档。 最后,它在控制点打印了一条新用户被创建的消息,并返回null表示函数已经结束。

Flutter 开发者: 可以增加捕获异常以及在控制台打印异常的代码吗?

Chat GPT: 没问题! 下面是增加了异常处理后的代码:

exports.userRegistration = functions.firestore
  .document("auth_users/{customerId}")
  .onCreate(async (snap, context) =>{
      const data = snap.data();
      const email = data.email;
      const name = data.name;
      const surname = data.surname;
      const displayName = name + ' ' + surname;
      const phone = data.phone;
      const address = data.address;

      try {
          // Create a random password for the new user
          const password = Math.random().toString(36).slice(-8);

          // Create a new Firebase Auth user with the provided email and password
          const user = await auth.createUser({
              email: email,
              password: password,
            });

          // Set the Firebase Auth display name to the name field in the document
          await auth.updateUser(user.uid, {
              displayName: name,
              phone: phone,
            });

          // Create a new document in the users collection with the same data as the original document
          await firestore.collection("users").doc(user.uid).set({
              email: email,
              name: name,
              surname: surname,
              displayName: displayName,
              phone: phone,
              address: address,
            });

          console.log(`New user with email ${email} and UID ${user.uid} has been created.`);
        } catch (error) {
          console.error(`Error creating user with email ${email}:`, error);
        }

      return null;
    });

Chat GPT: 我们给创建新的Firebase Auth用户和创建新的用户集合文档这部分代码增加了try-catch块。如果这个过程中出现了错误,将会捕获这个错误并且在控制台打印出来包含用户电子邮箱的错误信息。这会帮助你确认和解决用户注册过程中可能发生的问题。

  1. 解释代码

Flutter 开发者: 你能帮我解释一下这个代码吗:github.com/appstree-io…

Chat GPT: 这是一个使用Firebase作为后端的简单的聊天应用示例GitHub仓库代码。其中 firebase_service.dart 文件负责处理Flutter App与Firebase后端的通信。 下面是这个代码的简要说明: FirebaseService类:这是firebase_service.dart文件的主类。它包含了与Firebase交互的方法,例如创建新用户,获取一个用户的所有聊天会话,发送消息等等。 FirebaseFirestore类:这是Firebase SDK为Flutter提供的类,提供了一个与Firestore数据库交互的接口。FirebaseService类利用这个类与数据库进行交互。 UserModel类:这是代表聊天应用用户的一个简单的类,包括了用户ID、姓名、电子邮箱和头像等信息。 ChatModel类:这是代表聊天应用聊天会话的一个简单的类,包括了参与聊天的用户ID,最后发送的消息以及发送消息的时间戳。 MessageModel类:这是代表聊天应用消息的一个简单的类,包括了发送消息的用户ID,消息文本和消息的时间戳。 createUser方法:这个方法用于在Firebase里创建一个新用户。它接收UserModel对象作为输入,然后使用FirebaseFirestore类将用户添加到数据库。 getChats方法:这个方法获取用户的所有聊天会话。它使用用户ID作为输入,然后使用FirebaseFirestore类查询这个用户ID参与的所有聊天会话。 sendMessage方法:这个方法用于在聊天应用中发送消息。它使用MessageModel对象作为输入,然后使用FirebaseFirestore类往数据库里添加一条消息。 总的来说,firebase_service.dart给Flutter App提供了一个与Firebase简单通信的接口,并实现了一个简单的聊天功能。

Flutter 开发者:PlatformExceptionFirebaseAuthException 有什么区别?

Chat GPT:PlatformExceptionFirebaseAuthException 都代表了Flutter框架中使用Firebase授权认证的发生的错误. 二者主要的差别在于设计他们的目的和他们代表的错误类型: PlatformException: 这个异常是一个通用型的异常,用来表示发生了在Firebase中没有定义的错误。它是Flutter框架的一部分,典型的情况就是一个平台错误发生时抛出。例如,与设备的连接或存储的连接发生的错误 PlatformException 可以被任何与平台功能交互的插件或pacakge抛出。 FirebaseAuthException: 这是一个在于Firebase授权认证交互式发生的特定错误。当Firebase的授权认证服务出现错误时就会抛出FirebaseAuthException 错误,例如无效凭证、用户不存在或用户账户被禁用。 FirebaseAuthExceptionfirebase_auth的一部分,在 Firebase 授权认证模块定义。 总的来说,PlatformException是一个发生任何平台类型的错误时抛出,而FirebaseAuthException则是Firebase 授权认证的一个具体的错误。如果是通过Firebase来做授权认证,那么应该使用FirebaseAuthException。对于平台类型的错误,应该使用PlatformException

  1. 修复错误

Flutter 开发者: 我有如下的Widget:

GestureDetector(
  onTap: () async {
   myLocalNotifications.cancelNotification(context, reminder.id).then((value) {
    print('ddd');
    printNotification();
  });
  },

Flutter Developer: 我在其他类中也有这样的一个方法:

Future<void> cancelNotification(context, int notificationId) async {
  showDialog(
    context: context,
    builder: (BuildContext context) {
      return CustomAlertYesOrNo(
          alertTitle: 'Confirmation',
          alertText: 'Do you want to delete this reminder?',
          onConfirmed: () async {
            await flutterLocalNotificationsPlugin?.cancel(notificationId).then((value) {
              print('xxx');
              Navigator.pop(context);
            });
          }
      );
    },
  );
}

Flutter Developer: 点击后,这个函数执行正常,我也看到打印了“xxx”。然后,“ddd”并没有打印,为什么?我怎么样才能让这个代码能够执行?

Chat GPT: 为什么“ddd”没有打印的原因是以为它在cancelNotification()方法的.then()回调里面。很可能是因为某些原因导致这个then()回调没有执行。 为了调试这个问题,你可以尝试在cancelNotification()函数增加一个错误补偿的代码,看看是不是有错误抛出,例如像下面这样。如果没有抛出错误,你可以在CustomAlertYesOrNo组件里加一下打印语句,看看是否正常显示以及关闭。

await flutterLocalNotificationsPlugin?.cancel(notificationId)
  .then((value) {
    print('xxx');
    Navigator.pop(context);
  })
  .catchError((error) {
    print('Error cancelling notification: $error');
  });

Chat GPT: 另外,你也可以把then()回调从cancelNotification()移除,然后使用await的方式等待在打印“ddd”之前的取消操作完成,然后调用printNotification函数。通过这种方式,你可以确定取消操作在打印“ddd”和调用printNotification函数之前就已经完成,代码如下:

GestureDetector(
  onTap: () async {
    await myLocalNotifications.cancelNotification(context, reminder.id);
    print('ddd');
    printNotification();
  },
),

Flutter 开发者: 我移除了then回调,并且使用了await的方式等待取消操作完成。结果是先打印了“ddd”,然后才打印“xxx”。 Chat GPT: 如果在“xxx”之前先打印了“ddd”,意味着在cancelNotification函数中的取消操作耗时比在函数外的print语句要长。这可能是showDialog或其他比较复杂的操作导致UI线程出现了延迟。解决的办法是,你可以将print('ddd') printNotification()移到CustomAlertYesOrNo的回调方法onConfirmed内。这样的话,他们只能在用户确认取消操作完成后才执行。例如:

Future<void> cancelNotification(context, int notificationId) async {
  showDialog(
    context: context,
    builder: (BuildContext context) {
      return CustomAlertYesOrNo(
        alertTitle: 'Confirmation',
        alertText: 'Do you want to delete this reminder?',
        onConfirmed: () async {
          await flutterLocalNotificationsPlugin?.cancel(notificationId);
          print('xxx');
          Navigator.pop(context);
          print('ddd');
          printNotification();
        }
      );
    },
  );
}

Chat GPT: 这样的话,print('ddd')printNotification() 语句只会在用户确认取消操作完成后才会执行。 Flutter 开发者:我不能在onConfirmed里调用printNotification()。有其他解决方法吗? Chat GPT:如果你不能在onConfirmed里调用printNotification(),你可以通过一个回调方法通知父组件取消操作已经完成了,然后将notificationId作为一个参数传递过去。 例如,你可以修改cancelNotification()函数,接收一个Function(int)参数。这个函数会在取消操作完成后调用,从而触发printNotification()函数或在取消操作完成后的需要执行的其他代码。

Future<void> cancelNotification(context, int notificationId, Function(int) onCancelCompleted) async {
  showDialog(
    context: context,
    builder: (BuildContext context) {
      return CustomAlertYesOrNo(
        alertTitle: 'Confirmation',
        alertText: 'Do you want to delete this reminder?',
        onConfirmed: () async {
          await flutterLocalNotificationsPlugin?.cancel(notificationId);
          print('xxx');
          Navigator.pop(context);
          onCancelCompleted(notificationId);
        }
      );
    },
  );
}

Chat GPT:之后,你可以修改GestureDetectoronTap 回调方法,将需要执行的回调函数传递给cancelNotification函数:

GestureDetector(
  onTap: () async {
    await myLocalNotifications.cancelNotification(context, reminder.id, (notificationId) {
      print('ddd');
      printNotification();
    });
  },
),

Chat GPT: 这样的话,就能够保证print('ddd')printNotification() 只会在onCancelCompleted回调被调用,也就是取消操作确认完成后才执行。

  1. 编写文档

这里,使用了一个从GitHub随便找到一个Flutter App项目,然后要求Chat GPT来写一些文档。

Flutter 开发者: 你能给这个App写一份用户文档告诉用户怎么使用这个App吗?App链接: github.com/sarvesh4396… Chat GPT: 好的,很高兴能为你编写 Notepad Flutter App的用户文档。下面是使用这个App的分布指导:

如何使用Notepad App Notepad App是一个帮助你创建、编辑和删除笔记的非常简洁的App,下面是使用指南:

在你的手机上打开App 在主界面上,你会看到你的所有笔记的列表(如果你有创建过的话)。要添加一个新的笔记,点击屏幕右下角的绿色的加号按钮,然后你就可以进入到添加笔记的界面。你可以输入笔记的标题和内容,完成之后,点击“保存”按钮保存笔记。 要编辑已有的笔记,在主界面上点击这条编辑,然后就可以进入到编辑界面,你可以在编辑界面修改笔记的标题和内容。完成后点击“编辑”按钮可以保持编辑后的内容。 要删除笔记,在主界面上的一条笔记上向左滑动,这时候会出现一个“删除”按钮,点击这个按钮即可删除这条笔记。 就这样!Notepad App通过简单、直接的方式管理你的笔记。无论你是用于记录工作、学习还是个人其他用途,都可以让你一站式管理你的笔记。

  1. 给Dart文件添加注释

这个案例看看ChatGPT能不能帮助我们编写更易读的代码。我从GitHub上随便下载了一个Dart文件,然后让Chat GPT添加注释来重写代码。下面是原文件:

import 'dart:convert';
import 'package:chat_gpt_clone/models/api_model.dart';
import 'package:http/http.dart' as http;

class ApiService {
  Future<Map<String, dynamic>> getDataFromOpenAI(String query) async {
    final response = await http.post(
      Uri.parse(
        'https://api.openai.com/v1/engines/text-davinci-003/completions'),
      headers: {
        'Content-Type': contentType,
        'Authorization': 'Bearer $apiKey',
      },
      body: jsonEncode({
        'prompt': query,
        'max_tokens': 300,
        'temperature': 0,
      }),
    );

    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception('Failed to get data from OpenAI API');
    }
  }
}

Flutter 开发者: 你能给下面的代码添加注释吗: github.com/Siddharth-c… 。此外,我希望代码能够更加易读易懂。 Chat GPT: 没问题, 下面是添加注释后的更易读易懂的代码:

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:chat_gpt_clone/models/message.dart';

class ApiService {
  // Base URL for the API
  final String baseUrl = "https://api.openai.com/v1/";

  // API endpoint for generating a message
  final String endpoint = "engines/davinci-codex/completions";

  // API key for accessing the OpenAI API
  final String apiKey;

  ApiService({required this.apiKey});

  Future<Message> generateMessage({required String prompt}) async {
    // Define the headers for the API request
    final headers = {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer $apiKey',
    };

    // Define the request body for the API request
    final body = {
      'prompt': prompt,
      'max_tokens': 60,
      'temperature': 0.9,
    };

    // Make the API request and handle the response
    final response = await http.post(Uri.parse(baseUrl + endpoint),
                                     headers: headers, body: json.encode(body));
    if (response.statusCode == 200) {
      // If the response is successful, parse the JSON data and return a Message object
      final jsonBody = json.decode(response.body);
      return Message.fromJson(jsonBody['choices'][0]['text']);
    } else {
      // If the response is not successful, throw an error with the status code and message
      throw Exception(
        'Failed to generate message. Status code: ${response.statusCode}. Error message: ${response.body}');
    }
  }
}
  1. 编写一个简易的ToDo App

这是终极测试 —— 完成一个完整的Flutter ToDo App,下面是与Chat GPT的对话过程。

Flutter 开发者: 你好,能否帮我写一个基于Flutter的简单的待办事项提醒App。这个App需要列出所有的待办事项,然后有一个悬浮的按钮来添加新的待办事项。每个待办事项有一个标题、描述和到期日期。

Chat GPT: 没问题, 这是一个基础的待办事项提醒App,它列出了所有的待办事项,允许你通过一个悬浮按钮添加新的待办事项。待办事项的字段包括了标题、描述和到期时间。

首先,创建一个新的Flutter工程,然后打开main.dart,替换为下面的代码:

import 'package:flutter/material.dart';

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

class ReminderApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Reminder App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ReminderList(),
    );
  }
}

class ReminderList extends StatefulWidget {
  @override
  _ReminderListState createState() => _ReminderListState();
}

class _ReminderListState extends State<ReminderList> {
  List<Reminder> reminders = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Reminders'),
      ),
      body: ListView.builder(
        itemCount: reminders.length,
        itemBuilder: (BuildContext context, int index) {
          return ListTile(
            title: Text(reminders[index].title),
            subtitle: Text(reminders[index].description),
            trailing: Text(reminders[index].dueDate.toString()),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _addReminder(context),
        tooltip: 'Add Reminder',
        child: Icon(Icons.add),
      ),
    );
  }

  void _addReminder(BuildContext context) {
    TextEditingController titleController = TextEditingController();
    TextEditingController descriptionController = TextEditingController();
    DateTime dueDate;

    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Add Reminder'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: titleController,
                decoration: InputDecoration(
                  labelText: 'Title',
                ),
              ),
              TextField(
                controller: descriptionController,
                decoration: InputDecoration(
                  labelText: 'Description',
                ),
              ),
              ListTile(
                title: Text('Due Date'),
                subtitle: dueDate == null
                ? null
                : Text(dueDate.toString()),
                trailing: Icon(Icons.keyboard_arrow_down),
                onTap: () async {
                  DateTime picked = await showDatePicker(
                    context: context,
                    initialDate: DateTime.now(),
                    firstDate: DateTime.now(),
                    lastDate: DateTime(2100),
                  );
                  if (picked != null) {
                    setState(() {
                      dueDate = picked;
                    });
                  }
                },
              ),
            ],
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: Text('Cancel'),
            ),
            ElevatedButton(
              onPressed: () {
                Reminder reminder = Reminder(
                  title: titleController.text,
                  description: descriptionController.text,
                  dueDate: dueDate,
                );
                setState(() {
                  reminders.add(reminder);
                });
                Navigator.pop(context);
              },
              child: Text('Save'),
            ),
          ],
        );
      },
    );
  }
}

class Reminder {
  String title;
  String description;
  DateTime dueDate;

  Reminder({
    required this.title,
    required this.description,
    required this.dueDate,
  });
}

这个代码定义了一个新的ReminderApp组件来设置App的标题和主题,然后将主页设置为了ReminderList组件。

ReminderList组件是一个有状态组件,维护了待办事项列表。列表通过 ListView.builder显示。 每条待办事项使用了 ListTile 来显示标题、描述和到期日期。 ReminderList还包括了一个悬浮按钮,点击这个按钮会调研_addReminder 方法。点击按钮会显示了一个对话框,允许用户输入新的待办事项的标题、描述和到期日期。 当用户保存新的待办事项时,这条待办事项会添加到待办事项列表,然后组件会重建来更新待办事项列表。 最后,通过一个Reminder类来定义待办事项的属性,包括标题、描述和到期日期。 就这些了。通过这个基础的待办事项提醒App可以让你有个好的开始,并且可以扩展更多的特性。

看起来不错,但是运行后却报错了,于是我把错误复制给了Chat GPT。

Flutter 开发者: 谢谢,但是我得到了一个错误: Non-nullable variable ‘dueDate’ must be assigned before it can be used.

Chat CPT:很抱歉发生了这个错误!这是因为 dueDate 声明为一个非空的变量,但是没有给初始化的值。.下面是更新后的_addReminder方法,这里把 dueDate 初始化为 DateTime.now():

void _addReminder(BuildContext context) {
  TextEditingController titleController = TextEditingController();
  TextEditingController descriptionController = TextEditingController();
  DateTime dueDate = DateTime.now();

  showDialog(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text('Add Reminder'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: titleController,
              decoration: InputDecoration(
                labelText: 'Title',
              ),
            ),
            TextField(
              controller: descriptionController,
              decoration: InputDecoration(
                labelText: 'Description',
              ),
            ),
            ListTile(
              title: Text('Due Date'),
              subtitle: dueDate == null
                  ? null
                  : Text(dueDate.toString()),
              trailing: Icon(Icons.keyboard_arrow_down),
              onTap: () async {
                DateTime picked = await showDatePicker(
                  context: context,
                  initialDate: DateTime.now(),
                  firstDate: DateTime.now(),
                  lastDate: DateTime(2100),
                );
                if (picked != null) {
                  setState(() {
                    dueDate = picked;
                  });
                }
              },
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () {
              Reminder reminder = Reminder(
                title: titleController.text,
                description: descriptionController.text,
                dueDate: dueDate,
              );
              setState(() {
                reminders.add(reminder);
              });
              Navigator.pop(context);
            },
            child: Text('Save'),
          ),
        ],
      );
    },
  );
}

有趣!Chat GPT懂得分析错误,并且解释了原因和修正了代码错误。再次运行代码,却报了其他的错误,于是继续问 Chat GPT.

Flutter 开发者: 错误: A value of type ‘DateTime?’ can’t be assigned to a variable of type ‘DateTime’ because ‘DateTime?’ is nullable and ‘DateTime’ isn’t.

Chat GPT: 抱歉发生了错误。以为 dueDate声明为一个非空对象, 它不能赋予空值。然而,如果用户不选择日期,那从showDatePicker 会返回空值。要修复这个问题,需要给dueDate一个默认值() (如 DateTime.now()), 然后再重新赋值的时候检查是否为空,下面是更新后的代码。

void _addReminder(BuildContext context) {
  TextEditingController titleController = TextEditingController();
  TextEditingController descriptionController = TextEditingController();
  DateTime dueDate = DateTime.now();

  showDialog(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text('Add Reminder'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: titleController,
              decoration: InputDecoration(
                labelText: 'Title',
              ),
            ),
            TextField(
              controller: descriptionController,
              decoration: InputDecoration(
                labelText: 'Description',
              ),
            ),
            ListTile(
              title: Text('Due Date'),
              subtitle: dueDate == null
                  ? null
                  : Text(dueDate.toString()),
              trailing: Icon(Icons.keyboard_arrow_down),
              onTap: () async {
                DateTime? picked = await showDatePicker(
                  context: context,
                  initialDate: DateTime.now(),
                  firstDate: DateTime.now(),
                  lastDate: DateTime(2100),
                );
                if (picked != null) {
                  setState(() {
                    dueDate = picked;
                  });
                }
              },
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () {
              Reminder reminder = Reminder(
                title: titleController.text,
                description: descriptionController.text,
                dueDate: dueDate,
              );
              setState(() {
                reminders.add(reminder);
              });
              Navigator.pop(context);
            },
            child: Text('Save'),
          ),
        ],
      );
    },
  );
}

Flutter developer: 很棒,谢谢!你能在主页按卡片的形式显示待办事项吗,并且基于是否过期为每个待办事项添加一个不同的图标。同时,请格式化数据以只显示日期而不显示具体的时间。

Chat GPT: 好的, 这是更新后按卡片显示待办事项的代码,过去和未来到期的待办事项会有不同的图标,并且到期日期也格式化为只保留了日期部分:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Reminders'),
    ),
    body: ListView.builder(
      itemCount: reminders.length,
      itemBuilder: (BuildContext context, int index) {
        Reminder reminder = reminders[index];
        IconData iconData =
            reminder.dueDate.isAfter(DateTime.now()) ? Icons.access_time : Icons.watch_later;
        return Card(
          child: ListTile(
            leading: Icon(iconData),
            title: Text(reminder.title),
            subtitle: Text(
              DateFormat('yyyy-MM-dd').format(reminder.dueDate),
            ),
            trailing: IconButton(
              icon: Icon(Icons.delete),
              onPressed: () {
                setState(() {
                  reminders.removeAt(index);
                });
              },
            ),
          ),
        );
      },
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () => _addReminder(context),
      tooltip: 'Add Reminder',
      child: Icon(Icons.add),
    ),
  );
}

Chat GPT: 在这个版本中,ListView.builder 组件用来显示待办事项,对于每一个待办事项都使用Card组件来显示他们的标题、描述和到期日期。使用了一个 icon 来指示这个待办事项是否是过去的还是未来的。使用了 DateFormat 类将到期日期格式化为 yyyy-MM-dd, 这样只会显示日期,而不会显示具体的时间。

Chat GPT: 记住 DateFormat 类是 intl package中的,因此在使用之前需要引入这个package ,因此需要在你的Dart文件顶部添加下面导入的代码:

import 'package:intl/intl.dart';

结果是我在几分钟内写了一个完整功能的ToDo清单App,虽然是个非常基础的App,但是运行正常,下面是App的界面截图,看起来还是相当不错的。 image.png

总结

可以看到,Chat GPT是一个可以大幅提升Flutter 开发者生产力的伟大工具。在本文的案例中,Chat CPT成功搞定了我们的需求。

在某些案例下,我们需要使用2到3个问题才能得到需要的答案,也就是我们需要花点时间思考如何向Chat GPT提问,即便一开始你的问题不那么清晰,但是可以在后面追加问题来得的准确的答案。

我是岛上码农,微信公众号同名。如有问题可以加本人微信交流,微信号:island-coder

👍🏻:觉得有收获请点个赞鼓励一下!

🌟:收藏文章,方便回看哦!

💬:评论交流,互相进步!

本文正在参加「金石计划」