ReactNative【实战系列教程】我的小红书 6 -- 购物(含商品搜索、商品分类、商品列表)

最终效果

点击搜索输入框后

注意事项

商品搜索为背景透明的单独页面,便于代码解耦 !

代码实现

app/(tabs)/shop.tsx

c 复制代码
import AntDesign from "@expo/vector-icons/AntDesign";
import { useRouter } from "expo-router";
import {
  Dimensions,
  FlatList,
  Image,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from "react-native";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const ITEM_WIDTH = (SCREEN_WIDTH - 18) >> 1;
export default function ShopScreen() {
  const router = useRouter();
  const onSearchPress = () => {
    router.push("/searchGoods");
  };
  const goodsList = [
    {
      id: 1,
      title: "纯小麦粉面粉无小麦麸质低脂杂粮糖友敏宝牛油果果桥本·700g*3袋装",
      image:
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage109.360doc.com%2FDownloadImg%2F2021%2F06%2F1618%2F224298760_2_20210616063501614&refer=http%3A%2F%2Fimage109.360doc.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1754646069&t=46ad4dd7d2215498aecafa09558bac63",
      price: 51,
      originPrice: undefined,
      promotion: undefined,
    },
    {
      id: 2,
      title: "糙米米粉江西米线南仓拌粉螺狮粉800g粗粮无小麦麸",
      image:
        "https://ww4.sinaimg.cn/mw690/007I08aHly8hxw2fy44z2j30m80m8jtv.jpg",
      price: 84,
      originPrice: undefined,
      promotion: undefined,
    },
    {
      id: 3,
      title: "手作一鸣禅意大合集9.9r全新款绕指柔串12mm圆珠抹茶芝士大合集",
      image:
        "https://img2.baidu.com/it/u=1752777421,933296832&fm=253&app=138&f=JPEG?w=800&h=1200",
      price: 9.9,
      originPrice: 12.8,
      promotion: undefined,
    },
    {
      id: 4,
      title: "山核桃手串文玩核桃手串手持珠串小核桃串手挂可爆浆深山采核桃",
      image:
        "https://img1.baidu.com/it/u=1640157490,2252140535&fm=253&app=138&f=JPEG?w=500&h=736",
      price: 9.9,
      originPrice: undefined,
      promotion: undefined,
    },
    {
      id: 5,
      title: "纯色素珠5.6R拼手速·纯绿11*12mm",
      image:
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fbea04f46-dd1a-46f9-8c30-f98c3617b24d%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1754645565&t=621fb0466253ddda8b74888c6fd6c56f",
      price: 5.8,
      originPrice: undefined,
      promotion: undefined,
    },
    {
      id: 6,
      title: "天然绿檀木佛珠手串老料和田玉檀香木链男女民族饰品手串",
      image:
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fcbu01.alicdn.com%2Fimg%2Fibank%2FO1CN01EbmBWk1YtAzaWm0Tb_%21%211647433116-0-cib.jpg&refer=http%3A%2F%2Fcbu01.alicdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1754645594&t=e3bd317daf5611cdb2ef1781056cc775",
      price: 12,
      originPrice: undefined,
      promotion: undefined,
    },
    {
      id: 7,
      title: "黄油椰子素酥咸蛋黄酥脆饼干椰蓉酥糕点零食酥到没朋友咖啡椰子酥",
      image:
        "https://img2.baidu.com/it/u=2300908930,1727378078&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1067",
      price: 15.8,
      originPrice: undefined,
      promotion: "满100减18",
    },
    {
      id: 8,
      title: "厚切吐司面包奶香味提拉米苏味手撕面包14袋整箱装混合口味",
      image:
        "https://t13.baidu.com/it/u=3725456725,3923692805&fm=224&app=112&f=JPEG?w=500&h=500",
      price: 19.9,
      originPrice: 25.98,
      promotion: undefined,
    },
  ];
  const categoryList = [
    {
      id: 1,
      name: "蔬菜",
      image:
        "https://img01.yzcdn.cn/upload_files/2021/03/08/Fg6Qp_A8ScuIlSdv5ImHRAwvNFSk.jpg!middle.jpg",
    },
    {
      id: 2,
      name: "鞋子",
      image:
        "https://imgservice.suning.cn/uimg1/b2c/image/P5wpAyAvGZuwAuR7WqTFsg.jpg_800w_800h_4e",
    },
    {
      id: 3,
      name: "收纳盒",
      image:
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.alicdn.com%2Fbao%2Fuploaded%2Fi4%2F690591437%2FO1CN01SKPhW01MUBhz5wV73_%21%21690591437.jpg&refer=http%3A%2F%2Fimg.alicdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1754644995&t=96299f549a7ce4947700b3d11424be73",
    },
    {
      id: 4,
      name: "水果",
      image:
        "https://m.360buyimg.com/mobilecms/s750x750_jfs/t2641/47/1902721556/127381/3cfb874d/574e99a2Nce76f409.jpg!q80.jpg",
    },
    {
      id: 5,
      name: "酒水",
      image:
        "https://m.360buyimg.com/mobilecms/s750x750_jfs/t17581/178/845387461/322822/5428cef1/5aa9d5c7N8d0ad290.jpg!q80.jpg",
    },
    {
      id: 6,
      name: "饼干",
      image:
        "https://img01.yzcdn.cn/upload_files/2021/07/12/8db487f504549b2738ef3f77b7aa2232.jpg!middle.jpg",
    },
    {
      id: 7,
      name: "电脑椅",
      image:
        "https://pic.rmb.bdstatic.com/bjh/down/dc41f9f74d70e08b5877df601c381c42.jpeg@wm_2,t_55m+5a625Y+3L+enn+S4gOermeS6jOaJi+WKnuWFrOWutuWFtw==,fc_ffffff,ff_U2ltSGVp,sz_20,x_13,y_13",
    },
    {
      id: 8,
      name: "包包",
      image: "https://p4.zbjimg.com/task/2013-07/24/works/51ef5093662ef.jpg",
    },
    {
      id: 9,
      name: "行李箱",
      image:
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fcbu01.alicdn.com%2Fimg%2Fibank%2FO1CN014NJo3c26XMySVASfM_%21%212209736787671-0-cib.jpg&refer=http%3A%2F%2Fcbu01.alicdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1754645294&t=b0f52e5ea829f6b8c31b672446ec964b",
    },
    {
      id: 10,
      name: "鸡蛋",
      image:
        "https://img20.360buyimg.com/ceco/jfs/t1/149790/20/10889/119725/5f883afeE9f15bcae/e6611d4bc27acf78.jpg!q70.jpg",
    },
  ];
  const ListHeader = () => {
    const styles = StyleSheet.create({
      container: {
        width: "100%",
        flexDirection: "row",
        flexWrap: "wrap",
        padding: 10,
      },
      categoryItem: {
        width: "20%",
        alignItems: "center",
        paddingVertical: 16,
      },
      itemImg: {
        width: 40,
        height: 40,
        resizeMode: "contain",
      },
      itemNameTxt: {
        fontSize: 14,
        color: "#333",
        marginTop: 6,
      },
    });
    return (
      <View style={styles.container}>
        {categoryList.map((item) => {
          return (
            <View key={`${item.id}`} style={styles.categoryItem}>
              <Image style={styles.itemImg} source={{ uri: item.image }} />
              <Text style={styles.itemNameTxt}>{item.name}</Text>
            </View>
          );
        })}
      </View>
    );
  };
  const renderItem = ({ item }: { item: GoodsSimple; index: number }) => {
    const styles = StyleSheet.create({
      item: {
        width: ITEM_WIDTH,
        borderRadius: 8,
        overflow: "hidden",
        marginLeft: 6,
        marginTop: 6,
        marginBottom: 8,
      },
      img: {
        width: "100%",
        height: 200,
        resizeMode: "cover",
      },
      titleTxt: {
        fontSize: 14,
        color: "#333",
        marginTop: 6,
      },
      prefix: {
        fontSize: 14,
        color: "#333",
        fontWeight: "bold",
        marginTop: 4,
      },
      priceTxt: {
        fontSize: 22,
        color: "#333",
        fontWeight: "bold",
        textAlign: "justify",
      },
      originTxt: {
        fontSize: 13,
        color: "#999",
        fontWeight: "normal",
      },
      promotionTxt: {
        width: 78,
        fontSize: 12,
        color: "#999",
        borderRadius: 2,
        borderWidth: 1,
        borderColor: "#bbb",
        textAlign: "center",
        marginTop: 4,
      },
    });
    return (
      <View style={styles.item}>
        <Image style={styles.img} source={{ uri: item.image }} />
        <Text style={styles.titleTxt}>{item.title}</Text>
        {!!item.promotion && (
          <Text style={styles.promotionTxt}>{item.promotion}</Text>
        )}
        <Text style={styles.prefix}>
          ¥
          <Text style={styles.priceTxt}>
            {item.price}{" "}
            {!!item.originPrice && (
              <Text style={styles.originTxt}>原价:{item.originPrice}</Text>
            )}
          </Text>
        </Text>
      </View>
    );
  };
  return (
    <View style={styles.page}>
      <View style={styles.topBar}>
        <TouchableOpacity style={styles.searchBox} onPress={onSearchPress}>
          <AntDesign name="search1" size={18} color="grey" />
          <Text style={styles.searchTxt}>米粉</Text>
        </TouchableOpacity>
        <AntDesign
          style={styles.iconBtn}
          name="shoppingcart"
          size={22}
          color="grey"
        />
        <AntDesign
          style={styles.iconBtn}
          name="profile"
          size={22}
          color="grey"
        />
        <AntDesign
          style={styles.iconBtn}
          name="ellipsis1"
          size={22}
          color="grey"
        />
      </View>
      <FlatList
        style={{ flex: 1 }}
        data={goodsList}
        keyExtractor={(item) => `${item.id}`}
        extraData={[categoryList]}
        renderItem={renderItem}
        numColumns={2}
        ListHeaderComponent={<ListHeader />}
      />
    </View>
  );
}
const styles = StyleSheet.create({
  page: {
    width: "100%",
    height: "100%",
    backgroundColor: "white",
  },
  iconBtn: {
    marginLeft: 10,
  },
  topBar: {
    width: "100%",
    height: 40,
    flexDirection: "row",
    alignItems: "center",
    paddingHorizontal: 16,
  },
  searchBox: {
    height: 32,
    flex: 1,
    backgroundColor: "#f0f0f0",
    borderRadius: 16,
    flexDirection: "row",
    alignItems: "center",
    paddingHorizontal: 16,
  },
  searchTxt: {
    width: "100%",
    fontSize: 14,
    color: "#bbb",
    marginLeft: 10,
  },
});

app/searchGoods.tsx

背景色为透明!

c 复制代码
import AntDesign from "@expo/vector-icons/AntDesign";
import { useRouter } from "expo-router";
import React, { useEffect, useRef } from "react";
import {
  StyleSheet,
  Text,
  TextInput,
  TouchableOpacity,
  View,
} from "react-native";
export default function MineScreen() {
  const inputRef = useRef<TextInput>(null);
  const router = useRouter();
  useEffect(() => {
    setTimeout(() => {
      inputRef.current?.focus();
    }, 100);
  }, []);
  return (
    <View style={styles.page}>
      <View style={styles.topBar}>
        <TouchableOpacity
          style={styles.backButton}
          onPress={() => router.back()}
        >
          <AntDesign name="left" size={24} color="black" />
        </TouchableOpacity>
        <View style={styles.searchLayout}>
          <AntDesign name="search1" size={18} color="grey" />
          <TextInput
            ref={inputRef}
            style={styles.searchTxt}
            placeholder="纯粮小麦粉"
            placeholderTextColor={"#bbb"}
          />
        </View>
        <Text style={styles.searchBotton}>搜索</Text>
      </View>
    </View>
  );
}
const styles = StyleSheet.create({
  page: {
    width: "100%",
    height: "100%",
    backgroundColor: "transparent",
  },
  searchLayout: {
    height: 32,
    flex: 1,
    backgroundColor: "#f0f0f0",
    borderRadius: 16,
    flexDirection: "row",
    alignItems: "center",
    paddingHorizontal: 16,
    marginLeft: 16,
  },
  searchTxt: {
    height: "100%",
    fontSize: 14,
    color: "#bbb",
    marginLeft: 6,
    paddingHorizontal: 8,
    paddingVertical: 0,
    paddingTop: 8,
    textAlignVertical: "center",
  },
  searchBotton: {
    fontSize: 16,
    color: "#666",
    marginHorizontal: 12,
  },
  topBar: {
    display: "flex",
    width: "100%",
    height: 40,
    flexDirection: "row",
    alignItems: "center",
    backgroundColor: "white",
  },
  backButton: {
    height: "100%",
    paddingLeft: 16,
    justifyContent: "center",
  },
});

app/_layout.tsx

给商品搜索页注册路由,配置其为透明模式。

c 复制代码
              <Stack.Screen
                name="searchGoods"
                options={{
                  headerShown: false,
                  presentation: "transparentModal",
                }}
              />
相关推荐
Misha韩15 小时前
React Native 基础组件详解<二>
react native·组件
朝阳391 天前
ReactNative【实战】瀑布流布局列表(含图片自适应、点亮红心动画)
react native
_一两风3 天前
深入理解 React 事件机制与 DOM 事件系统
react native·react.js
Misha韩3 天前
React Native 初始化项目和模拟器运行
react native
Misha韩5 天前
React Native 亲切的组件们(函数式组件/class组件)和陌生的样式
react native·函数式组件·class组件
1234Wu5 天前
React Native 接入 eCharts
javascript·react native·react.js
wen's6 天前
React Native 0.79.4 中 [RCTView setColor:] 崩溃问题完整解决方案
javascript·react native·react.js
朝阳397 天前
ReactNative【实战系列教程】我的小红书 3 -- 自定义底栏Tab导航(含图片选择 expo-image-picker 的使用)
react native
冰冷的bin7 天前
【React Native】自定义倒计时组件CountdownView
react native