fluttercommunity / community

Flutter Community - A central place for community made Flutter content.
1.56k stars 121 forks source link

Issue with go router - go to details page #142

Open Schokiii93 opened 4 months ago

Schokiii93 commented 4 months ago

Hi guys,

I've developed course manager app that fits specific requirements of a sports club that I am a member of. The app fulfills the following main function:

Users (or participants in our sports group) can navigate from the homepage to an overview page that lists all available courses. By clicking on one of the courses, you will be taken to a detail page where you can then register for and cancel the course.

I use firebase for the data structure. Each event is assigned a unique ID from the firestore collection.

Now I would like to implement a "Share" button on the detail page. By clicking on it, a link should be generated and moved to the clipboard so that I can then send it via Whatsapp, for example. Users who click on it should be taken directly to the details page.

I have already defined the following routes:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  await dotenv.load();

  await FirebaseAppCheck.instance.activate(
    webProvider:
        ReCaptchaV3Provider('####'),
    //androidProvider: AndroidProvider.debug,
    androidProvider: AndroidProvider.playIntegrity,
    appleProvider: AppleProvider.deviceCheck,
  );

  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  /// Constructs a [MyApp]
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [Locale('de', 'DE')],
      debugShowCheckedModeBanner: false,
      title: 'myApp',
      theme: getThemeData(),
    );
  }
}

final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

/// The route configuration.
final GoRouter _router = GoRouter(
  navigatorKey: navigatorKey,
  initialLocation: '/',
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      builder: (BuildContext context, GoRouterState state) {
        return StreamBuilder(
          stream: FirebaseAuth.instance.authStateChanges(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Loading();
            }
            if (snapshot.hasData) {
              return Consumer(
                builder: (BuildContext context, WidgetRef ref, Widget? child) {
                  final userProfileAsync = ref.watch(userProfileProvider);
                  return userProfileAsync.when(
                    data: (userData) {
                      if (userData != null) {
                        //check if user has already been activated
                        bool accountActivated = userData.accountActivated;
                        return accountActivated
                            ? const MainScreen()
                            : const AccountNotActivatedScreen();
                      } else {
                        //other events
                        return Center(
                          child: Loading(),
                        );
                      }
                    },
                    loading: () => Scaffold(body: Loading()),
                    error: (error, stackTrace) {
                      return const Text(
                          'Fehler beim Abrufen der Benutzerdaten');
                    },
                  );
                },
              );
            }
            return const LoginScreen();
          },
        );
      },
      routes: <RouteBase>[
        GoRoute(
          path: 'events/:eventId',
          builder: (BuildContext context, GoRouterState state) {
            final id = state.pathParameters['eventId']!;
            return EventDetailsPage(eventId: id);
          },
        ),
      ],
    ),
  ],
);

As you can see, there is a GoRoute defined which gets the eventId (the event id comes from firestore).

Futhermore, I created a details page. This is the place where the share button should be included. I think the only interesting thing should be the "_copyLinkToClipboard" function and the share button I added:

class EventDetailsPage extends ConsumerStatefulWidget {
  final String eventId;
  const EventDetailsPage({super.key, required this.eventId});

  @override
  ConsumerState<EventDetailsPage> createState() => _EventDetailsPageState();
}

class _EventDetailsPageState extends ConsumerState<EventDetailsPage> {
...some code

  @override
  void initState() {
    super.initState();
    initializeDateFormatting('de_DE'); //initialize system language
    ...some more code
  }

  void _copyLinkToClipboard(BuildContext context) {
    String currentUrl = 'paravertikale-augsburg.de/event/${widget.eventId}';
    Clipboard.setData(ClipboardData(text: currentUrl));
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Link zur Veranstaltung kopiert')),
    );
  }

  @override
  Widget build(BuildContext context) {
    final streamData = ref.read(userProfileProvider);
    final futureEvent = ref.watch(eventDetailsProvider(widget.eventId));

    return Scaffold(
      body: _isLoading
          ? Platform.isIOS
              ? const Center(
                  child:
                      CupertinoActivityIndicator(radius: 20, animating: true))
              : Loading()
          : futureEvent.when(
              loading: () => Platform.isIOS
                  ? const Center(
                      child: CupertinoActivityIndicator(
                          radius: 20, animating: true))
                  : Loading(),
              error: (error, stack) => Text("Error: $error"),
              data: (eventData) {
                return streamData.when(
                  error: (error, stackTrace) => Text(error.toString()),
                  loading: () => Platform.isIOS
                      ? const Center(
                          child: CupertinoActivityIndicator(
                              radius: 20, animating: true))
                      : Loading(),
                  data: ((data) => Stack(
                        children: [
                          CustomScrollView(
                            slivers: [

                              ...some boring extra code in between

                                                    Expanded(
                                                        flex: 5,
                                                        child: AutoSizeText(
                                                          maxFontSize: 18,
                                                          maxLines: 2,
                                                          "Dieser Kurs wird $_lowerCaseRecurrenceType wiederholt",
                                                          textAlign:
                                                              TextAlign.start,
                                                        )),
                                                    const SizedBox(width: 20),

                                                    **IconButton(
                                                      onPressed: () {
                                                        _copyLinkToClipboard(
                                                            context);
                                                      },
                                                      icon: const Icon(
                                                          FontAwesomeIcons
                                                              .share),
                                                    ),**

                                               ... and lots of more code

I added this to my androidmanisfest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:label="ParaVertikale" android:name="${applicationName}" android:icon="@mipmap/ic_launcher">
        <activity android:name=".MainActivity" android:enableOnBackInvokedCallback="true" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" android:roundIcon="@mipmap/round_launcher">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme"/>
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="http" android:host="paravertikale-augsburg.de"/>
                <data android:scheme="https" android:host="paravertikale-augsburg.de"/>
                <data android:pathPattern="/event/.*" />
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data android:name="flutterEmbedding" android:value="2"/>
    </application>
</manifest>

The thing is, I don't know why it doesn't work and I don't know how to generate a dynamic link that points to the event details.

Can anyone help me with this? Thanks and regards Dennis