0

用Flutter写了个博客园APP - xiaoyaocz

 1 year ago
source link: https://www.cnblogs.com/xiaoyaocz/p/16978177.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

用Flutter写了个博客园APP

最近在摸鱼时看到了一些博客园API文章,就想着摸鱼时写个APP练练手。

现阶段实现了以下功能模块:

  • 博客浏览、评论
  • 闪存浏览、发布、评论

博问暂时只支持浏览,不支持回答提问等操作。

支持iOS、Android平台。

浅色模式:

Light.jpg

深色模式:

Dark.jpg

开发前需要先到https://api.cnblogs.com/申请API KEY,申请通过才能使用博客园的API。

输入一下个人信息跟应用信息,提交后等待博客园的审核。通过审核后会收到一封包含ClientId和ClientSecret的邮件。

申请APIKEY.png

博客园API文档上的API不全,有些API需要到Github中查询。

API申请完成就可以愉快的进行开发了。
Flutter版本这次选择了最新的3.3,基于GetX框架+Dio来开发,不得不说GetX一把梭的感觉是真的爽。

目录结构跟GetX框架差不多,按我自己的习惯进行了一些改动:

  • app 一些通用的类及样式
  • services 提供数据存储等服务
  • requests 请求的封装
  • generated 生成的国际化文件,使用 get generate locales生成
  • modules 模块,每个会有两个文件,view及controller
  • widgets 自定义的小组件
  • routes 路由定义
  • models 实体类

个人练手项目就加上了一些以前没用过包跟特性体验一下(如lottie、getx国际化)。

这个项目比较简单,技术上没什么好说的,主要说下博文的展示:

博文由于是HTML富文本且有一些复杂样式,把文章内容转为Flutter Widget显示的体验挺差,所以还是使用WebView来展示。

在这里我选择了flutter_inappwebview包,这个包会比官方的webview_flutter功能更加丰富,更新也更频繁。

HTML加载流程如下:

  1. 在assets中添加HTML,预留内容、js、css槽位
  2. 在assets中添加两个css样式,分别对应浅色模式及深色模式;css内容可以从任意文章的Web页面抓取
  3. 在assets中添加JS,编写WebView与APP的交互
  4. 在assets中添加highlight.js,实现代码的高亮
  5. 将js、css填充至HTML,css需要根据APP主题选择
  6. 通过API读取博文内容,并填充到HTML中
  7. 通过webViewController.loadData(data: html)在APP中显示博文

具体实现可以看源代码。实现效果:

博文.jpg

由于项目依赖WebView(文章、登录),所以只支持iOS、Android平台。要支持其他平台也不难,只需要把文章内容转为Flutter Widget显示再更改下登录逻辑即可。

使用Github Actions可以很简单的自动完成打包发布的步骤。

关于Github Actions的介绍,可以看下官方文档

在项目创建根目录创建一个.github/workflows,然后创建一个xxxx.yml,写入以下代码。

name: app-build-action
#推送Tag时触发
on:
  push:
    tags:
      - "*"
jobs:
  build-ios-android:
    # 使用macOS镜像,如果不需要打包iOS,可以替换成Ubuntu
    runs-on: macos-latest
    permissions:
      contents: write
    steps:
      #签出代码 
      - uses: actions/checkout@v2
        with:
          ref: master
       #写出ENV文件
      - name: Create .env
        run: |
          echo "CLIENT_ID=${{ secrets.CLIENT_ID }}" > .env
          echo "CLIENT_SECRET=${{ secrets.CLIENT_SECRET }}" >> .env
      #APK签名设置
      - name: Download Android keystore
        id: android_keystore
        uses: timheuer/[email protected]
        with:
          fileName: keystore.jks
          encodedString: ${{ secrets.KEYSTORE_BASE64 }}
      - name: Create key.properties
        run: |
          echo "storeFile=${{ steps.android_keystore.outputs.filePath }}" > android/key.properties
          echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> android/key.properties
          echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
          echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
      #设置JAVA环境
      - uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: "12.x"
          cache: 'gradle'
      #设置Flutter
      - name: Flutter action
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.3.9'
          cache: true 
      - name: Restore packages
        run: flutter pub get
      #打包APK
      - name: Build APK
        run: flutter build apk --release
      #上传APK至Artifacts
      - name: Upload APK to Artifacts
        uses: actions/upload-artifact@v3
        with:
          name: app-release.apk
          path: |
            build/app/outputs/flutter-apk/app-release.apk
      #打包iOS
      - name: Build IPA
        run: flutter build ios --release --no-codesign
      #创建未签名ipa
      - name: Create IPA
        run: |
          mkdir build/ios/iphoneos/Payload
          cp -R build/ios/iphoneos/Runner.app build/ios/iphoneos/Payload/Runner.app
          zip -q -r build/ios/iphoneos/ios_no_sign.ipa build/ios/iphoneos/Payload
      #上传IPA至Artifacts
      - name: Upload IPA to Artifacts
        uses: actions/upload-artifact@v3
        with:
          name: ios_no_sign.ipa
          path: |
            build/ios/iphoneos/ios_no_sign.ipa
      #从document/new_version.json读取版本信息
      - name: Read version
        id: version
        uses: juliangruber/read-file-action@v1
        with:
          path: document/new_version.json
      - name: Echo version
        run: echo "${{ fromJson(steps.version.outputs.content).version }}"
      - name: Echo version content
        run: echo "${{ fromJson(steps.version.outputs.content).version_desc }}"
      #发布至Github Release
      - name: Upload Release
        uses: ncipollo/release-action@v1
        with:
          allowUpdates: true
          artifactErrorsFailBuild: true
          artifacts: "build/app/outputs/flutter-apk/app-release.apk,build/ios/iphoneos/ios_no_sign.ipa"
          name: "${{ fromJson(steps.version.outputs.content).version }}"
          body: "${{ fromJson(steps.version.outputs.content).version_desc }}"
          token: ${{ secrets.TOKEN }}
      #完成
      - run: echo "🍏 This job's status is ${{ job.status }}."

运行脚本前,需要先到Github项目设置-Secrets-Actions中添加以下字段

CLIENT_ID 博客园API ClientId
CLIENT_SECRET 博客园API ClientSecret
KEYSTORE_BASE64 APK签名文件,转为BASE64
KEY_ALIAS APK签名keyAlias
KEY_PASSWORD APK签名keyPassword
STORE_PASSWORD APK签名storePassword
TOKEN Github Token

Github Token点这里添加,注意需要权限的设置。

secrets.png

设置完成后,当想要打包一个新版本时,只需要提交代码并打上Tag推送至Github,即可自动打包。

这个项目没啥难度且博客园API文档还是比较全面,基本对着API做个UI就行了。

天冷变懒了,有些功能还没有写完,过段时间再慢慢完善了。

项目开源地址:https://github.com/xiaoyaocz/flutter_cnblogs,有兴趣可以到Release中下载安装包体验一下。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK