Flutter實(shí)戰(zhàn):構(gòu)建類似ChatGPT的聊天應(yīng)用(四)

Flutter AI 訪問多個(gè)AI 模型

在上一篇中我們構(gòu)建了一個(gè)可以訪問多模型聊天應(yīng)用京腥,現(xiàn)在設(shè)置菜單可以選擇不同的大模型

因?yàn)榇a較多肉瓦,沒有將所有代碼貼出(也沒有上傳GitHub)如果有需要在在評論區(qū)留言,我會更新文章或者上傳代碼

模型選擇

我創(chuàng)建了一個(gè)名為 _showPopupMenu 的函數(shù),在用戶點(diǎn)擊 IconButton 時(shí)調(diào)用該函數(shù),然后使用 showMenu 函數(shù)顯示一個(gè)類似于 PopupMenuButton 的菜單臣嚣。用戶選擇了某個(gè) AI 類型后會更新標(biāo)題和相應(yīng)的 AI 服務(wù)實(shí)例。

我增加一個(gè) _showAISelectionMenu 方法

寫法 1

  void _showAISelectionMenu(BuildContext context) {
    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) {
        return Container(
          child: Wrap(
            children: <Widget>[
              ListTile(
                leading: Icon(Icons.chat),
                title: Text('ChatGPT'),
                onTap: () {
                  setState(() {
                    _appBarTitle = 'ChatGPT'; // 更新標(biāo)題
                    _aiProvider = AIProvider(type: 'ChatGPT'); // 更新 AIProvider
                  });
                  Navigator.pop(context);
                },
              ),
              ListTile(
                leading: Icon(Icons.android),
                title: Text('文心一言'),
                onTap: () {
                  setState(() {
                    _appBarTitle = '文心一言'; // 更新標(biāo)題
                    _aiProvider = AIProvider(type: 'ErnieBot'); // 更新 AIProvider
                  });
                  Navigator.pop(context);
                },
              ),
              ListTile(
                leading: Icon(Icons.question_answer),
                title: Text('通義千問'),
                onTap: () {
                  setState(() {
                    _appBarTitle = '通義千問'; // 更新標(biāo)題
                    _aiProvider = AIProvider(type: 'QwenBot'); // 更新 AIProvider
                  });
                  Navigator.pop(context);
                },
              ),
              // 添加更多的 AI 類型
            ],
          ),
        );
      },
    );
  }
  
  //...
}

這樣寫方法中重復(fù)代碼太多了剥哑,我將 AI 類型和對應(yīng)的標(biāo)題定義為兩個(gè)列表 aiTypes 和 aiTitles硅则。然后在 _showAISelectionMenu 方法中使用 ListView.builder 來遍歷這兩個(gè)列表,生成對應(yīng)的 ListTile 列表株婴。同時(shí)怎虫,我添加了一個(gè)輔助方法 _buildIcon 來根據(jù) AI 類型生成對應(yīng)的圖標(biāo)。這樣可以減少重復(fù)的代碼困介,并使代碼更加清晰易讀大审。

寫法 2

void _showAISelectionMenu(BuildContext context) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext context) {
      return Container(
        child: ListView.builder(
          itemCount: aiTypes.length,
          itemBuilder: (BuildContext context, int index) {
            String aiType = aiTypes[index];
            String aiTitle = aiTitles[index];
            return ListTile(
              leading: _buildIcon(aiType),
              title: Text(aiTitle),
              onTap: () {
                setState(() {
                  _appBarTitle = aiTitle; // 更新標(biāo)題
                  _aiProvider = AIProvider(type: aiType); // 更新 AIProvider
                });
                Navigator.pop(context);
              },
            );
          },
        ),
      );
    },
  );
}

Widget _buildIcon(String aiType) {
  switch (aiType) {
    case 'ChatGPT':
      return Icon(Icons.chat);
    case 'ErnieBot':
      return Icon(Icons.android);
    case 'QwenBot':
      return Icon(Icons.question_answer);
    default:
      return Icon(Icons.help);
  }
}

final List<String> aiTypes = ['ChatGPT', 'ErnieBot', 'QwenBot'];
final List<String> aiTitles = ['ChatGPT', '文心一言', '通義千問'];

具體模型選擇

寫法 1

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _textController = TextEditingController();
  final List<Message> _messages = [];

  AIProvider _aiProvider = AIProvider();
  String _appBarTitle = 'ChatGPT'; // 初始標(biāo)題

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_appBarTitle),
        actions: [
          IconButton(
            icon: Icon(Icons.settings),
            onPressed: () {
              _showPopupMenu(context);
            },
          ),
        ],
      ),
      body: Container(
        //...
      ),
    );
  }

  void _showPopupMenu(BuildContext context) async {
    final selectedValue = await showMenu<String>(
      context: context,
      position: RelativeRect.fromLTRB(100, 100, 0, 100), // 根據(jù)需要調(diào)整位置
      items: [
        PopupMenuItem<String>(
          value: 'ChatGPT',
          child: Text('ChatGPT'),
        ),
        PopupMenuItem<String>(
          value: 'ErnieBot',
          child: Text('文心一言'),
        ),
        PopupMenuItem<String>(
          value: 'QwenBot',
          child: Text('通義千問'),
        ),
        // 添加更多的 AI 類型
      ],
    );

    if (selectedValue != null) {
      setState(() {
        _appBarTitle = selectedValue == 'QwenBot' ? '通義千問' : selectedValue; // 更新標(biāo)題
        _aiProvider = AIProvider(type: selectedValue); // 更新 AIProvider
      });
    }
  }

  //...
}

這樣寫方法中重復(fù)代碼太多了,我們可以使用 List.generate 來生成 PopupMenuButton 的子菜單項(xiàng)座哩,通過循環(huán)遍歷 aiTypes 列表徒扶,并使用每個(gè)元素創(chuàng)建一個(gè) PopupMenuItem。這樣就可以實(shí)現(xiàn)通過循環(huán)構(gòu)造 PopupMenuButton 的子菜單項(xiàng)根穷。

寫法 2

void _showPopupMenu(BuildContext context) {
  showMenu(
    context: context,
    position: RelativeRect.fromLTRB(0, AppBar().preferredSize.height, 0, 0),
    items: List.generate(aiTypes.length, (index) {
      return PopupMenuItem<String>(
        value: aiTypes[index],
        child: Text(aiTitles[index]),
      );
    }),
    elevation: 8.0,
  ).then((value) {
    if (value != null) {
      setState(() {
        _appBarTitle = value == 'QwenBot' ? '通義千問' : value; // 更新標(biāo)題
        _aiProvider = AIProvider(type: value); // 更新 AIProvider
      });
    }
  });
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姜骡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子屿良,更是在濱河造成了極大的恐慌圈澈,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尘惧,死亡現(xiàn)場離奇詭異康栈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)褥伴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漾狼,“玉大人重慢,你說我怎么就攤上這事⊙吩辏” “怎么了似踱?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我核芽,道長囚戚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任轧简,我火速辦了婚禮驰坊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哮独。我一直安慰自己拳芙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布皮璧。 她就那樣靜靜地躺著舟扎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悴务。 梳的紋絲不亂的頭發(fā)上睹限,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音讯檐,去河邊找鬼羡疗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛裂垦,可吹牛的內(nèi)容都是我干的顺囊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蕉拢,長吁一口氣:“原來是場噩夢啊……” “哼特碳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晕换,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤午乓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后闸准,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體益愈,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年夷家,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒸其。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡库快,死狀恐怖摸袁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情义屏,我是刑警寧澤靠汁,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布蜂大,位于F島的核電站,受9級特大地震影響蝶怔,放射性物質(zhì)發(fā)生泄漏奶浦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一踢星、第九天 我趴在偏房一處隱蔽的房頂上張望澳叉。 院中可真熱鬧,春花似錦斩狱、人聲如沸耳高。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泌枪。三九已至,卻和暖如春秕岛,著一層夾襖步出監(jiān)牢的瞬間碌燕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工继薛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留修壕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓遏考,卻偏偏與公主長得像慈鸠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子灌具,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容