Add a shippingCost per lineItem rather than per Order
You're right that the shipping calculator API doesn't lend itself to different rates per lineItem as such, but I'm pretty sure you can still make it do what you want.  (But longer term I agree that getLineItemRate(lineItem) or similar should be an option!).

Basically, for now, you can do anything you like in the getBaseRate() and return the total from that to the cart.  (Personally I implement all my shipping stuff as services so I can call them from controllers etc too (e.g. to do shipping quotes via a controller separate to the cart as such - but in this I'll do ti right here).  But you can also do stuff like save data to the order in here, which should be enough to make this work for you.

Basically, you'd loop through the lineItems, looking at the value in the dropdown for each and using that to sum up your shipping costs.  You probably want to list out the chosen options and costs for each lineitem separately on that payment page, so you'll also need to save that data somewhere too...so create a holder variable lineItemShippingOptions in your order for that data and set it as part of the process so you can refer to it in the later templates.

(I'd use a non-editable incognito field for that so your order processors can't accidentally change the data later - https://github.com/mmikkel/IncognitoField-Craft (I have PR-ed an improvement to that for just this sort of thing).

(Obviously - this is untested sketch code!)

public function getBaseRate()
{
   $shippingCost = 0;
   $dataToSave = [];

   $this-&gt;cart = craft()-&gt;commerce_cart-&gt;getCart();
   foreach ($cart-&gt;lineItems as $item){
        $shippingOptionChosen = item.options.whatever;
        if($shippingOptionChosen == "service1" ){
            $shippingCost = $20;
            $dataToSave[item.id] = ['chosenOption'=&gt;$shippingOptionChosen, 'chosenPrice'=&gt;20];   
        }
        elseif ($shippingOptionChosen == "service2"){
            etc 
        }
   }
   //Save this data in to the order
   $this-&gt;cart-&gt;setContentFromPost(['lineItemShippingOptions'=&gt;$dataToSave]);
}


Then in your templates you can refer to lineItemShippingOptions to print the info out to the customer.  (That's probably not the ideal way to build the template data you're saving but should serve the purpose to demonstrate the idea...basically you can build any format array you like really).

I use this same approach in a very complicated custom shipping calculator (crazily this is some 2000 lines of php!) - that deals with multiple services (including some table data ones and some live quote ones), plus grouping &amp; boxing services &amp; caching to make it all work as quickly as possible (live rates always being much slower of course...).  Because at times the checkout flow gets awkward relying on Commerce's template tags, I also have an actionShippingOptions controller that calls the same services and returns the quotes &amp; data that way (also very handy for debugging purposes!) - but you may well not need that.  But even with the existing API you can do damn near anything you like between setting the rate and saving data to the order and showing it later, basically.