timsneath / landmarks_flutter

BSD 3-Clause "New" or "Revised" License
8 stars 1 forks source link

Notes on an iOS 14-style inset list #13

Open timsneath opened 1 year ago

timsneath commented 1 year ago

Took me multiple attempts, but I finally produced an accurate-ish representation of the main landmarks page:

class LandmarksPage extends StatelessWidget {
  final List<Landmark> landmarks;

  const LandmarksPage({super.key, required this.landmarks});

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: CustomScrollView(
        semanticChildCount: landmarks.length,
        slivers: [
          CupertinoSliverNavigationBar(
            stretch: true,
            border: null,
            largeTitle: Text('Landmarks'),
            backgroundColor: CupertinoColors.systemGroupedBackground,
          ),
          SliverFillRemaining(
            hasScrollBody: false,
            child: CupertinoListSection.insetGrouped(children: [
              for (final landmark in landmarks) LandmarkRow(landmark: landmark),
            ]),
          ),
        ],
      ),
    );
  }
}

Comparison Images

I'm pleased to report that both dark and light mode worked without extra fuss. Overscroll also seems to be hooked up correctly.

Flutter SwiftUI
Flutter SwiftUI
Flutter SwiftUI

Notes on differences

  1. The row dividers in Flutter start at an indeterminate point, and I wasn't able to fix this.
  2. There's an ugly gap between the navbar and the content, which I couldn't reduce any further.
  3. It took a while to get this right. The defaults don't lead to an accurate rendition without several changes, as you can see above. Thank goodness for the CupertinoSliverNavigationBar example! I was getting overflow issues for a long time until I happened upon the hasScrollBody: false property in SliverFillRemaining.

All things considered, I'll call this a "success" based on my ability to get there eventually! However, this is significantly more verbose and complex than the SwiftUI equivalent:

var body: some View {
    NavigationView {
        List(landmarks) { landmark in
            NavigationLink {
                LandmarkDetail(landmark: landmark)
            } label: {
                LandmarkRow(landmark: landmark)
            }
        }.navigationTitle("Landmarks")
    }
}

(And I'm even being slightly generous to Flutter here, since the SwiftUI equivalent includes the onTap event that in the Flutter version is in the LandmarkRow widget.)

Piinks commented 1 year ago

Thanks for sharing!

There's an ugly gap between the navbar and the content, which I couldn't reduce any further.

I am wondering if this is because the outer MediaQueryData is wrapping the NavBar internally, and it may be propagating the space below from the notch (will look!).

Using SliverFillRemaining means the items are not lazily loaded, but that looks to be more of an issue with CupertinoListSection. Could result in a performance issue if there are many many items, and lots of unnecessary work for Flutter to do. Filed https://github.com/flutter/flutter/issues/119558

However, this is significantly more verbose and complex than the SwiftUI equivalent

True! I think often where Flutter is more verbose than a native implementation, it is because Flutter is more abstract in order to account for all native implementations folks may want to recreate. :)

Piinks commented 1 year ago

I am wondering if this is because the outer MediaQueryData is wrapping the NavBar internally

That would be a negative hunch, it looks like this extra space is in the CupertinoListSection itself.

timsneath commented 1 year ago

Should we file a separate issue for the spacing issue? I'm just the muppet who notes problems, not someone with any skills at fixing them :)

Piinks commented 1 year ago

Filed https://github.com/flutter/flutter/issues/121543 👍