I have Flutter application - simple online player that uses flutter_carplay and audio_service. I release this app to iOS only.
This the entry point of the app:
void main() async {
await App.initApp(); // basic initialization (Firebase, ...)
runApp(const App());
}
This is App
widget.
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (BuildContext context, Widget? child) {},
home: HomePage()
);
}
}
All the CarPlay related code can be found in HomePage
:
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final FlutterCarplay _flutterCarplay = FlutterCarplay();
@override
void initState() {
super.initState();
initCarPlay();
}
@override
Widget build(BuildContext context) {
return ...;
}
void initCarPlay() {
FlutterCarplay.setRootTemplate(
rootTemplate: CPListTemplate(
sections: [
CPListSection(header: "Section 1", items: [
CPListItem(text: "Item 1"),
CPListItem(text: "Item 2"),
CPListItem(text: "Item 3"),
]),
CPListSection(header: "Section 2", items: [
CPListItem(text: "Item 4"),
CPListItem(text: "Item 5"),
CPListItem(text: "Item 6"),
])
],
showsTabBadge: false,
systemIcon: "house.fill",
),
animated: true,
);
_flutterCarplay.forceUpdateRootTemplate();
_flutterCarplay.addListenerOnConnectionChange(_onCarplayConnectionChange);
}
void _onCarplayConnectionChange(CPConnectionStatusTypes status) {
//
}
@override
void dispose() {
_flutterCarplay.removeListenerOnConnectionChange();
super.dispose();
}
}
I have tried to call initCarPlay()
in the main()
directly, but the result was the same.
Firstly, you need to make sure that your initCarPlay
method is called even when the app is started from CarPlay
.
Furthermore, handle CarPlay-specific
lifecycle events to initialize your CarPlay
interface appropriately and ensure the necessary data and services are initialized in the background, so the app is ready when launched from CarPlay
.
Modified main
function
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await App.initApp();
runApp(const App());
}
App
Widget
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (BuildContext context, Widget? child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: child!,
);
},
home: const HomePage(),
);
}
}
Modified HomePage
Screen
Ensure initCarPlay
is called regardless of how the app is launched.
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final FlutterCarplay _flutterCarplay = FlutterCarplay();
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) => initApp());
_flutterCarplay.addListenerOnConnectionChange(_onCarplayConnectionChange);
}
Future<void> initApp() async {
initCarPlay();
}
void initCarPlay() {
FlutterCarplay.setRootTemplate(
rootTemplate: CPListTemplate(
sections: [
CPListSection(header: "Section 1", items: [
CPListItem(text: "Item 1"),
CPListItem(text: "Item 2"),
CPListItem(text: "Item 3"),
]),
CPListSection(header: "Section 2", items: [
CPListItem(text: "Item 4"),
CPListItem(text: "Item 5"),
CPListItem(text: "Item 6"),
])
],
showsTabBadge: false,
systemIcon: "house.fill",
),
animated: true,
);
_flutterCarplay.forceUpdateRootTemplate();
}
void _onCarplayConnectionChange(CPConnectionStatusTypes status) {
if (status == CPConnectionStatusTypes.connected) {
initCarPlay();
}
}
@override
void dispose() {
_flutterCarplay.removeListenerOnConnectionChange();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Home Page Content'),
),
);
}
}