본문 바로가기

IT IS IT/Flutter

[Flutter] ChatGPT API를 활용하여 대화 앱 만들기

728x90
반응형

챗 지피티를 활용하여 대화를 하는 앱을 만들어보자.

 

전체 소스는 아래와 같다.

저기서 apiKey 부분에 내 OpenAI에서 발급받은 API Key를 삽입하면 된다.

 

여기서 발전시키고 싶다면 채팅 초기화 등의 기능을 넣거나 초기 봇 세팅값 설정 기능 등을 넣어도 좋다.

채팅 초기화는 그냥 'chatbotSetting' 값만 비워주고 다시 gptSystemStting값을 할당해주기만 해도 된다.

 

 

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

//gpt API key
String apiKey = "내 API 키를 이곳에 넣는다.";

//gpt 최초 설정
String gptSystemSetting =
    "너는 영어 가이드다. 내가 적은 한글 문장을 영어로 번역하고 한글로 잘못된 부분을 설명해야 한다.";

//gpt 값 세팅
var chatbotStartValue = [
  {"role": "system", "content": gptSystemSetting},
];

var chatbotSetting = [chatbotStartValue.first];

//채팅 초기화
void chatReset() {
  chatbotSetting.clear();
  chatbotSetting.add(chatbotStartValue.first);
}

//초기값 부여작업
class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const GptTest(),
    );
  }
}

class GptTest extends StatefulWidget {
  const GptTest({super.key});

  @override
  State<GptTest> createState() => _GptTestState();
}

class _GptTestState extends State<GptTest> {
  ///답변 생성하는 함수
  Future<void> generateText(String prompt) async {
    chatbotSetting.add({"role": "user", "content": prompt!});
    debugPrint("prompt : $prompt");

    final response = await http.post(
      Uri.parse("https://api.openai.com/v1/chat/completions"),
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer $apiKey'
      },
      body: jsonEncode({
        "model": "gpt-3.5-turbo",
        "messages": chatbotSetting,
      }),
    );

    Map<String, dynamic> newresponse =
        jsonDecode(utf8.decode(response.bodyBytes));
    String chatbotAnswer = newresponse['choices'][0]['message']['content'];
    chatbotSetting.add({"role": "assistant", "content": chatbotAnswer});

    debugPrint("답변 : $chatbotAnswer");
    setState(() {});
  }

  TextEditingController userPrompt = TextEditingController(); //글자 전달을 위한 컨트롤러

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(15.0),
        child: Column(
          children: [
            Expanded(
              child: ListView.builder(
                itemCount: chatbotSetting.length,
                itemBuilder: (context, index) {
                  //유저 질문시에
                  if (chatbotSetting[index]["role"] == "user") {
                    return userInput(value: chatbotSetting[index]["content"]!);
                  }
                  //챗봇 답변시에
                  else if (chatbotSetting[index]["role"] == "assistant") {
                    return botAnswer(
                      value: chatbotSetting[index]["content"] ?? "답변이 없습니다.",
                    );
                  }
                  //기본 세팅값은?
                  else if (chatbotSetting[index]["role"] == "system") {
                    return botSetting(value: chatbotSetting[index]["content"]!);
                  }
                  //예외는 이런식으로
                  else {
                    return Text("정체불명 : ${chatbotSetting[index]["content"]}");
                  }
                },
              ),
            ),
            //질문 창
            Container(
              padding: const EdgeInsets.all(15),
              decoration: BoxDecoration(
                  color: Colors.lime[100],
                  borderRadius: BorderRadius.circular(10)),
              child: Row(
                children: [
                  Expanded(
                    child: TextField(
                      controller: userPrompt,
                    ),
                  ),
                  const SizedBox(
                    width: 15,
                  ),
                  InkWell(
                    onTap: () {
                      setState(() {
                        generateText(userPrompt.text);
                        userPrompt.text = "";
                      });
                    },
                    child: Container(
                        padding: const EdgeInsets.all(8),
                        decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(5),
                            color: Colors.amber),
                        child: const Text("GPT에게 전송!")),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Widget userInput({required String value}) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      const SizedBox(
        height: 30,
      ),
      const Icon(
        Icons.face,
        color: Colors.blue,
      ),
      Container(
        padding: const EdgeInsets.all(15),
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(10), color: Colors.blue),
        child: Text(
          value,
          style: const TextStyle(color: Colors.white),
        ),
      )
    ],
  );
}

Widget botAnswer({required String value}) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.end,
    children: [
      const Icon(
        Icons.school_outlined,
        color: Colors.green,
      ),
      Container(
        padding: const EdgeInsets.all(15),
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(10), color: Colors.green),
        child: Text(
          value,
          style: const TextStyle(color: Colors.white),
        ),
      )
    ],
  );
}

Widget botSetting({required String value}) {
  return Column(
    children: [
      const Text(
        "🤖",
        style: TextStyle(fontSize: 50),
      ),
      Container(
        padding: const EdgeInsets.all(30),
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(10), color: Colors.grey[800]),
        child: Text(
          value,
          style: const TextStyle(
              color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
        ),
      )
    ],
  );
}
728x90
반응형