[Flutter] 복사 가능!GoogleMap+ 카드 매끄러운 UI 만드는 방법

41433 단어 Fluttertech

이런 UI 하고 싶어요?



"카드와 기호가 결합된 UI를 만들고 싶습니다."
"그런데 아래 카드 부분은 어떻게 해요?
번뇌가 계속되는 반세기.드디어 그 방법을 찾았어...(매우 간단)

메서드


GoogleMap+Card
언뜻 보기에는 카드 부분은 설치하기 어렵지만 알면 간단합니다.
카드 부분PageView 실시!
Google Map에서 PageView를 Stack으로 겹치는 것일 뿐입니다.
PageView에서는 가장자리를 보는 느낌과 카드를 닦는 동작을 쉽게 구현할 수 있습니다!그다음에 교환할 때 임의의 기호로 카메라를 움직인다는 운동 연합!그럼 전선 한번 볼까요?

이루어지다


Shop 클래스 만들기


지도에 가게 정보를 표시하고 샵 등급을 만들려고 합니다.
이름과 좌표 정보를 가지고 있습니다.
class Shop {
  String uid;
  double latitude;
  double longitude;
  String name;

  Shop(this.uid, this.latitude, this.longitude, this.name);
}

Shop 목록 만들기


이번엔 현지에서 샵의 리스트를 제작한다.실제로 파이어스토어와 DB에 매장 정보를 미리 등록해서 가져갈 것 같아요.이번에는 홋카이도의 명소를 적절히 설정했다
final shops = [
  Shop('1', 43.0779575, 141.337819, '北海道大学'),
  Shop('2', 43.0692162, 141.3473406, '175°DENO坦々麺札幌駅北口店'),
  Shop('3', 43.05432, 141.3517185, 'UTAGE SAPPORO'),
  Shop('4', 43.0673817, 141.3416878, 'ラーメン二郎札幌店'),
  Shop('5', 43.072069, 141.331253, '焼肉と料理シルクロード'),
];

Map 섹션


나는 마크스의 부분이 중요해질 것이라고 생각한다.아까 List을 기호로 그려주세요.
또 표지판을 클릭할 때 아래 카드에 가볍게 두드린 가게를 표시할 수 있다.
 Widget _mapSection() {
    return GoogleMap(
      mapType: MapType.normal,
      initialCameraPosition: _initialCameraPosition,
      onMapCreated: (GoogleMapController controller) {
        _mapController = controller;
      },
      markers: shops.map(
        (selectedShop) {
          return Marker(
            markerId: MarkerId(selectedShop.uid),
            position: LatLng(selectedShop.latitude, selectedShop.longitude),
            icon: BitmapDescriptor.defaultMarker,
            onTap: () async {
              //タップしたマーカー(shop)のindexを取得
              final index = shops.indexWhere((shop) => shop == selectedShop);
              //タップしたお店がPageViewで表示されるように飛ばす
              _pageController.jumpToPage(index);
            },
          );
        },
      ).toSet(),
    );
  }

카드(PageView) 섹션


아래 카드 섹션을 PageView로 표시합니다.
onPageChanged교환 후 List<shop>의index를 얻었다.패지뷰를 통해 교환 후 중간에 나타나는 샵을 알 수 있다는 것이다.그리고 Google Map Controller로 카메라를 그 좌표까지 이동합니다!
 Widget _cardSection() {
    return Container(
      height: 148,
      padding: const EdgeInsets.fromLTRB(0, 0, 0, 20),
      child: PageView(
        onPageChanged: (int index) async {
          //スワイプ後のページのお店を取得
          final selectedShop = shops.elementAt(index);
          //現在のズームレベルを取得
          final zoomLevel = await _mapController.getZoomLevel();
          //スワイプ後のお店の座標までカメラを移動
          _mapController.animateCamera(
            CameraUpdate.newCameraPosition(
              CameraPosition(
                target: LatLng(selectedShop.latitude, selectedShop.longitude),
                zoom: zoomLevel,
              ),
            ),
          );
        },
        controller: _pageController,
        children: _shopTiles(),
      ),
    );
  }
	
  //カード1枚1枚について
  List<Widget> _shopTiles() {
    final _shopTiles = shops.map(
      (shop) {
        return Card(
          child: SizedBox(
            height: 100,
            child: Center(
              child: Text(shop.name),
            ),
          ),
        );
      },
    ).toList();
    return _shopTiles;
  }	

지도와 카드를 겹치다


각각 Stack으로 중첩됩니다.
 
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.bottomCenter,
      children: [
        _mapSection(),
        _cardSection(),
      ],
    );
  }	

복사 붙여넣기 ok!전체 텍스트 샘플 코드


main.dart
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Google Maps Demo',
      home: MapSample(),
    );
  }
}

class MapSample extends StatefulWidget {
  const MapSample({Key? key}) : super(key: key);

  
  State<MapSample> createState() => MapSampleState();
}

class MapSampleState extends State<MapSample> {
  late GoogleMapController _mapController;

  final _pageController = PageController(
    viewportFraction: 0.85,//0.85くらいで端っこに別のカードが見えてる感じになる
  );

  //初期位置を札幌駅に設定してます
  final CameraPosition _initialCameraPosition = const CameraPosition(
    target: LatLng(43.0686606, 141.3485613),
    zoom: 12,
  );

  
  void initState() {
    super.initState();
  }

  
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.bottomCenter,
      children: [
        _mapSection(),
        _cardSection(),
      ],
    );
  }

  Widget _mapSection() {
    return GoogleMap(
      mapType: MapType.normal,
      initialCameraPosition: _initialCameraPosition,
      onMapCreated: (GoogleMapController controller) {
        _mapController = controller;
      },
      markers: shops.map(
        (selectedShop) {
          return Marker(
            markerId: MarkerId(selectedShop.uid),
            position: LatLng(selectedShop.latitude, selectedShop.longitude),
            icon: BitmapDescriptor.defaultMarker,
            onTap: () async {
              //タップしたマーカー(shop)のindexを取得
              final index = shops.indexWhere((shop) => shop == selectedShop);
              //タップしたお店がPageViewで表示されるように飛ばす
              _pageController.jumpToPage(index);
            },
          );
        },
      ).toSet(),
    );
  }

  Widget _cardSection() {
    return Container(
      height: 148,
      padding: const EdgeInsets.fromLTRB(0, 0, 0, 20),
      child: PageView(
        onPageChanged: (int index) async {
          //スワイプ後のページのお店を取得
          final selectedShop = shops.elementAt(index);
          //現在のズームレベルを取得
          final zoomLevel = await _mapController.getZoomLevel();
          //スワイプ後のお店の座標までカメラを移動
          _mapController.animateCamera(
            CameraUpdate.newCameraPosition(
              CameraPosition(
                target: LatLng(selectedShop.latitude, selectedShop.longitude),
                zoom: zoomLevel,
              ),
            ),
          );
        },
        controller: _pageController,
        children: _shopTiles(),
      ),
    );
  }

  List<Widget> _shopTiles() {
    final _shopTiles = shops.map(
      (shop) {
        return Card(
          child: SizedBox(
            height: 100,
            child: Center(
              child: Text(shop.name),
            ),
          ),
        );
      },
    ).toList();
    return _shopTiles;
  }
}

/// お店の情報を持つクラス。マップに表示させるために座標を持たせている
class Shop {
  String uid;
  double latitude;
  double longitude;
  String name;

  Shop(this.uid, this.latitude, this.longitude, this.name);
}

/// 北海道の名所
final shops = [
  Shop('1', 43.0779575, 141.337819, '北海道大学'),
  Shop('2', 43.0692162, 141.3473406, '175°DENO坦々麺札幌駅北口店'),
  Shop('3', 43.05432, 141.3517185, 'UTAGE SAPPORO'),
  Shop('4', 43.0673817, 141.3416878, 'ラーメン二郎札幌店'),
  Shop('5', 43.072069, 141.331253, '焼肉と料理シルクロード'),
];

총결산


어때요?개인적으로 Widget 사용법을 알면 그렇게 어렵지 않을 거라고 생각해요!Fulutter의 UI 제작에서 Widget의 지식은 매우 중요합니다. 모르면 큰 차이가 있을 수 있다는 것을 알기 때문에 모든 Widget를 간단하게 접촉하는 것이 좋습니다!

좋은 웹페이지 즐겨찾기