MST

星途 面试题库

面试题:Flutter复杂业务场景下跨平台导航架构设计

设想一个具有多模块、多层级导航的复杂业务应用,如电商类应用,需要适配iOS、Android以及Web平台用户习惯。请设计一套完整的导航架构,涵盖页面跳转逻辑、导航栏样式切换、平台特定交互处理等方面,并且详细说明如何通过代码实现解耦,以便于后续的维护和新平台的适配。
48.5万 热度难度
前端开发Flutter

知识考点

AI 面试

面试题答案

一键面试

导航架构设计

  1. 页面跳转逻辑
    • 基于路由表:创建一个全局的路由表,每个页面都有一个唯一的标识符(例如字符串路径)。在电商应用中,商品列表页可能是 /product/list,商品详情页是 /product/detail/:id,其中 :id 是商品的唯一标识。
    • 导航栈管理:在原生平台(iOS 和 Android),使用导航栈来管理页面的进出。例如在 iOS 中使用 UINavigationController,在 Android 中使用 FragmentManager 结合 FragmentTransaction。Web 平台可以通过浏览器的历史记录 API 来模拟类似的导航栈效果。
    • 模态跳转:对于一些独立的功能页面,如登录、注册、支付等,采用模态跳转的方式。这种方式可以在不影响当前导航栈的情况下展示新页面,完成操作后关闭模态页面回到原页面。
  2. 导航栏样式切换
    • 平台共性:设计一个基础的导航栏样式,包括标题显示区域、返回按钮(如果需要)、操作按钮区域。导航栏的背景颜色、文字颜色等基本样式可以保持一致,以提供统一的视觉体验。
    • iOS 特定:遵循 iOS 的设计规范,导航栏高度、按钮样式等按照苹果的指导方针进行设计。例如,返回按钮通常在左上角,样式简洁明了。
    • Android 特定:符合 Android 的 Material Design 规范,导航栏的样式可能在不同版本的 Android 系统中有细微差异,要确保适配。例如,操作按钮可能以图标形式出现在右上角,且风格更扁平。
    • Web 特定:Web 导航栏可以更灵活,根据不同的屏幕尺寸和布局进行响应式设计。可以采用固定在顶部或者随页面滚动等不同的展示方式。
  3. 平台特定交互处理
    • iOS:利用 iOS 系统提供的手势交互,如侧滑返回上一页。同时,注意处理不同设备的屏幕尺寸和分辨率,确保页面布局合理。
    • Android:支持 Android 系统的物理返回键操作,在页面栈管理中正确响应返回键事件。另外,考虑到 Android 设备的多样性,对不同屏幕比例和分辨率进行适配。
    • Web:处理浏览器的前进、后退按钮事件,与导航栈保持同步。同时,针对触摸设备和鼠标设备提供不同的交互方式,如触摸设备支持滑动操作,鼠标设备支持悬停等操作。

代码实现解耦

  1. 分层架构
    • 业务逻辑层:处理应用的核心业务逻辑,如数据获取、处理和业务规则等。这一层不依赖于具体的平台和导航实现,保持独立。
    • 导航抽象层:创建一个抽象的导航服务接口,定义页面跳转、导航栏操作等通用方法。例如:
// Java 示例
public interface NavigationService {
    void navigateTo(String route);
    void setNavigationBarTitle(String title);
    // 其他导航相关方法
}
// Swift 示例
protocol NavigationService {
    func navigate(to route: String)
    func setNavigationBarTitle(_ title: String)
    // 其他导航相关方法
}
- **平台实现层**:针对每个平台(iOS、Android、Web)实现上述导航服务接口。例如在 iOS 中使用 `UINavigationController` 来实现页面跳转:
class iOSNavigationService: NavigationService {
    let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func navigate(to route: String) {
        // 根据路由表找到对应的视图控制器并跳转
        guard let viewController = getViewController(for: route) else { return }
        navigationController.pushViewController(viewController, animated: true)
    }

    func setNavigationBarTitle(_ title: String) {
        navigationController.topViewController?.title = title
    }

    private func getViewController(for route: String) -> UIViewController? {
        // 路由表查找逻辑
        // 例如根据路由返回对应的视图控制器实例
        return nil
    }
}

在 Android 中使用 FragmentManager 实现类似功能:

class AndroidNavigationService implements NavigationService {
    private final FragmentManager fragmentManager;

    public AndroidNavigationService(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
    }

    @Override
    public void navigateTo(String route) {
        // 根据路由表找到对应的 Fragment 并替换
        Fragment fragment = getFragmentForRoute(route);
        if (fragment!= null) {
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.replace(R.id.fragment_container, fragment);
            transaction.addToBackStack(null);
            transaction.commit();
        }
    }

    @Override
    public void setNavigationBarTitle(String title) {
        // 设置 Toolbar 的标题
        Toolbar toolbar = findViewById(R.id.toolbar);
        if (toolbar!= null) {
            toolbar.setTitle(title);
        }
    }

    private Fragment getFragmentForRoute(String route) {
        // 路由表查找逻辑
        // 例如根据路由返回对应的 Fragment 实例
        return null;
    }
}

在 Web 中使用路由库(如 React Router)实现:

import { useHistory } from'react-router-dom';

class WebNavigationService {
    constructor() {
        this.history = useHistory();
    }

    navigate(to route) {
        this.history.push(route);
    }

    setNavigationBarTitle(title) {
        // 设置页面标题或导航栏标题的逻辑
        document.title = title;
    }
}
  1. 依赖注入:通过依赖注入的方式将导航服务注入到需要进行导航操作的业务组件中。例如在 iOS 中,可以在视图控制器的初始化方法中传入导航服务实例:
class ProductListViewController: UIViewController {
    let navigationService: NavigationService

    init(navigationService: NavigationService) {
        self.navigationService = navigationService
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func showProductDetail(productId: String) {
        let route = "/product/detail/\(productId)"
        navigationService.navigate(to: route)
    }
}

在 Android 中,可以在 Fragment 中通过构造函数或依赖注入框架(如 Dagger)获取导航服务:

public class ProductListFragment extends Fragment {
    private NavigationService navigationService;

    public ProductListFragment(NavigationService navigationService) {
        this.navigationService = navigationService;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 视图创建逻辑
        return inflater.inflate(R.layout.fragment_product_list, container, false);
    }

    public void showProductDetail(String productId) {
        String route = "/product/detail/" + productId;
        navigationService.navigateTo(route);
    }
}

在 Web 应用中,同样可以通过依赖注入的方式将导航服务传递给组件:

function ProductListComponent({ navigationService }) {
    const showProductDetail = (productId) => {
        const route = `/product/detail/${productId}`;
        navigationService.navigate(route);
    };

    return (
        // 组件渲染逻辑
    );
}

通过这种分层架构和依赖注入的方式,业务逻辑与导航实现解耦,便于后续维护和新平台的适配。新平台只需要实现导航抽象层的接口,就可以无缝集成到现有应用中。