king 发布的文章

结论

总的来说能用,但是官方文档不是很全,基本靠猜

生成步骤

定义接口

specs目录下创建一个接口定义

import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
import { TurboModuleRegistry } from 'react-native';

// 定义模块的接口
export interface Spec extends TurboModule {
    // 显示简单弹窗
    showAlert(title: string, message: string): void;
}

export default TurboModuleRegistry.getEnforcing<Spec>('NativeAlert');

添加配置

packege.json中增加

  "codegenConfig": {
    "name": "NativeAlertSpec",
    "type": "modules",
    "jsSrcsDir": "./specs",
    "android": {
      "javaPackageName": "com.nativealert"
    }
  }

生成一些模板代码

cd android && ./gradlew generateCodegenArtifactsFromSchema

建立安卓代码目录

这个目录要和配置中的javaPackageName对应: android/app/src/main/java/com/nativealert

完成模块逻辑

java的文件名和类名一致即可

package com.nativealert

import android.app.AlertDialog
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
import com.facebook.proguard.annotations.DoNotStrip
import com.nativealert.NativeAlertSpec
import javax.annotation.Nonnull

class NativeAlertModule(reactContext: ReactApplicationContext) : NativeAlertSpec(reactContext) {
   
    companion object {
        const val NAME = "NativeAlert"
    }

    override fun getName(): String {
        return NAME
    }

    @ReactMethod
    @DoNotStrip
    override fun showAlert(title: String?, message: String?) {
        val currentActivity = reactApplicationContext.currentActivity ?: return

        AlertDialog.Builder(currentActivity)
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton("OK") { dialog, _ ->
                dialog.dismiss()
            }
            .show()
    }
}

完成注册代码

package com.nativealert

import com.facebook.react.BaseReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.nativealert.NativeAlertModule

class NativeAlertPackage : BaseReactPackage() {

    override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
        if (name == NativeAlertModule.NAME) {
            NativeAlertModule(reactContext)
        } else {
            null
        }

    override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
        mapOf(
            NativeAlertModule.NAME to ReactModuleInfo(
                name = NativeAlertModule.NAME,
                className = NativeAlertModule::class.java.name,
                canOverrideExistingModule = false,
                needsEagerInit = true,
                isCxxModule = false,
                isTurboModule = true
            )
        )
    }
}

MainApplication.kt中注册

这里的add(NativeAlertPackage())默认是有注释的,要放开

package com.iguojin.tlp

import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.nativealert.NativeAlertPackage

class MainApplication : Application(), ReactApplication {

  override val reactHost: ReactHost by lazy {
    getDefaultReactHost(
      context = applicationContext,
      packageList =
        PackageList(this).packages.apply {
          // Packages that cannot be autolinked yet can be added manually here, for example:
          add(NativeAlertPackage())
        },
    )
  }

  override fun onCreate() {
    super.onCreate()
    loadReactNative(this)
  }
}

页面中调用

import NativeAlert from '../../specs/NativeAlert';

NativeAlert.showAlert("测试", "内容")

  • 纯色大背景
  • 所有元素不带border,仅用背景色改变来表现层次关系
  • 页面上基本只有2层,上层使用白色透明背景色 rgba(255, 255, 255, 0.1)
  • 用圆角大小表现温暖程度
  • 通过按钮等交互元素来体现主色调
  • 文字使用白色
  • 文字使用大小体现重要程度
  • 各个间距使用4的倍数,界面会更和谐

这样的设计,会展现出一种偏冷的极简风,开发简单,用户也能接受

网络配置

  • 默认的nat网络可以从虚拟机访问宿主机,需要配置端口映射才能访问到虚拟机,所以先配置好2222:22,方便本地登录
  • 配置另一个网卡为桥接网络,需要手动配置ip
    /etc/netplan/50-cloud-init.yaml

    network:
    version: 2
    ethernets:
      enp0s3:
        dhcp4: true
    
      enp0s8:  # 网卡名称(你提供的)
        dhcp4: false  # 禁用 DHCP
        addresses: [192.168.0.100/24]  # 静态 IP,与本机同网段,100 不要与主机 8 冲突
        gateway4: 192.168.0.1  # 与本机默认网关一致
        nameservers:
          addresses: [8.8.8.8, 114.114.114.114]  # DNS 服务器
  • 2026-01-04开启桥接网络结果速度很慢

暂时禁用这个桥接的网卡

# 查看所有网卡(确认要禁用的接口名,如 enp0s8)
ip link show

# 禁用指定网卡(替换 enp0s8 为你的接口名)
sudo ip link set enp0s8 down

# 验证状态(应显示 "DOWN")
ip link show enp0s8

  • 2025-12-28
    安装multipass后无法被局域网设备发现,可以在hyperv设置一个外部网络,再通过multipass list看到ip
    无法挂载目录,需要配置multipass set local.privileged-mounts=true
    遇到bug:挂载目录后在虚拟机中无法被看到,挂载时候一直转没有成功和失败,后来发现需要安装multipass-sshfs

这是一个flutter项目,请帮我设计并开发一个高颜值教育app首页

渐变背景,Container + BoxDecoration 的 LinearGradient,纵向渐变
卡片式设计,卡片要有层次(带圆角阴影)
Tab导航+底部按钮用的 ConvexBottomBar 插件,横向渐变
代码中不要硬编码颜色,请使用谷歌material自带的语义化配色,主色调设为天青色

问题

  1. 总是不分模块
  2. 颜色总是不用自带的
  3. 总是自己处理深色模式isDark之类的

请增加一种淡粉色作为点缀,依然遵循语义化原则,tabbar横向这两种颜色的渐变
请为每个卡片增加一种青春气息的背景色,但是要保证文字清晰

  • 2026-01-17
    今天遇到一个SizeBox设置了宽度但是放进SilverPadding里面还是会把宽度拉到百分百,暂时给这个组件一个定位就会好了,没有研究清楚
  • 2026-01-19
    输入框页面进入另一个页面返回后总是自动获得焦点,原来是返回时,flutter会恢复页面状态,他会把之前有焦点的聚焦
    只要跳转前_textFocusNode.unfocus();即可
  • 2026-01-22

动画后测量元素

在一个动画结束后,已经触发了AnimationStatus.completed的情况下,去测量这个元素的尺寸和位置,我发现有时候并不对,AI回答这和异步渲染有关,没有深究,暂时从下一帧即可准备获取

// 获取卡牌的全局坐标
WidgetsBinding.instance.addPostFrameCallback((_) {
  // 保证所有渲染都完成了
  _handleCardOut(index);
});

坐标转换

可以先从组件里获取到localToGlobal的全局坐标,然后到了需要的地方使用globalToLocal

  • 我遇到setState之后,立即获取元素尺寸失败,貌似是setState之后页面在更新,此时无法立即获取尺寸,需要等到下一帧

子类不会自动获得父类的构造函数,而是不显示写构造函数时候,给了一个无参数的构造函数

2026-01-24

经常包等待dart虚拟机,暂时可以先重启

taskkill /f /im dartvm.exe

安卓镜像设置

  • android/build.gradle

    buildscript {
      ext {
          buildToolsVersion = "36.0.0"
          minSdkVersion = 24
          compileSdkVersion = 36
          targetSdkVersion = 36
          ndkVersion = "27.1.12297006"
          kotlinVersion = "2.1.20"
      }
      repositories {
          maven { url 'https://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
          google()
          mavenCentral()
      }
      dependencies {
          classpath("com.android.tools.build:gradle")
          classpath("com.facebook.react:react-native-gradle-plugin")
          classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
      }
    }
    
    allprojects {
      repositories {
          maven { url 'https://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
          google()
          mavenCentral()
      }
    }
    
    apply plugin: "com.facebook.react.rootproject"
  • android/gradle/wrapper/gradle-wrapper.properties

    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-9.0.0-bin.zip
    networkTimeout=10000
    validateDistributionUrl=true
    zipStoreBase=GRADLE_USER_HOME
    zipStorePath=wrapper/dists

建立ts项目

https://reactnative.dev/docs/getting-started-without-a-framework

npx @react-native-community/cli@latest init AwesomeProject --pm yarn --package-name 包名

开发环境打包

yarn android --active-arch-only

指定架构打包

./gradlew installRelease -PreactNativeArchitectures=arm64-v8a

2025-12-17

今天rn程序突然无法调试
后来发现由于我在AndroidManifest.xml中增加了允许http访问的属性,不知为何就没法调试了,暂时先不管了

rn 0.83.0页面切换总是白屏

原因未知,在MainActivity中加了代码

  /**
    * 【新增代码】
    * 重写 onCreate 方法,在加载JS应用之前设置窗口背景色。
    * 这能有效改善启动时的白屏/闪屏现象。
    */
  override fun onCreate(savedInstanceState: Bundle?) {
      // 先调用父类方法
      super.onCreate(savedInstanceState)
      // 将根窗口的背景色设置为 #111827
      window?.decorView?.setBackgroundColor(Color.parseColor("#111827"))
  }

2025-12-18

发现rn用的nativewind,不支持tailwin的round-2xl之类的,只能用round-xl
rn键盘弹起上推页面很难
view里py-4无效py-6却有效
最后发现是tailwind.config中没有把新的目录加进来

运行中产生很多日志,可用如下方法立即删除

sudo truncate -s 0 $(sudo docker inspect --format='{{.LogPath}}' 容器id)